Added Statistics calculation
Statistics now show calculated values
This commit is contained in:
parent
fe87374e47
commit
fc0f69dacb
2147 changed files with 141321 additions and 39 deletions
21
node_modules/reselect/LICENSE
generated
vendored
Normal file
21
node_modules/reselect/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2018 Reselect Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
206
node_modules/reselect/README.md
generated
vendored
Normal file
206
node_modules/reselect/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
# Reselect
|
||||
|
||||
[![npm package][npm-badge]][npm][![Coveralls][coveralls-badge]][coveralls][![GitHub Workflow Status][build-badge]][build]![TypeScript][typescript-badge]
|
||||
|
||||
A library for creating memoized "selector" functions. Commonly used with Redux, but usable with any plain JS immutable data as well.
|
||||
|
||||
- Selectors can compute derived data, allowing [Redux] to store the minimal possible state.
|
||||
- Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
|
||||
- Selectors are composable. They can be used as input to other selectors.
|
||||
|
||||
The **Redux docs usage page on [Deriving Data with Selectors](https://redux.js.org/usage/deriving-data-selectors)** covers the purpose and motivation for selectors, why memoized selectors are useful, typical Reselect usage patterns, and using selectors with [React-Redux].
|
||||
|
||||
## Installation
|
||||
|
||||
### Redux Toolkit
|
||||
|
||||
While Reselect is not exclusive to [Redux], it is already included by default in [the official Redux Toolkit package](https://redux-toolkit.js.org) - no further installation needed.
|
||||
|
||||
```ts
|
||||
import { createSelector } from '@reduxjs/toolkit'
|
||||
```
|
||||
|
||||
### Standalone
|
||||
|
||||
For standalone usage, install the `reselect` package:
|
||||
|
||||
```bash
|
||||
# NPM
|
||||
npm install reselect
|
||||
|
||||
# Yarn
|
||||
yarn add reselect
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
The Reselect docs are available at **https://reselect.js.org**, and include usage guides and API references:
|
||||
|
||||
- [**Introduction**](#https://reselect.js.org/introduction/getting-started)
|
||||
- [**How Does Reselect Work?**](#https://reselect.js.org/introduction/how-does-reselect-work)
|
||||
- **API Reference**:
|
||||
- **[`createSelector`]**
|
||||
- **[`createSelectorCreator`]**
|
||||
- **[`createStructuredSelector`]**
|
||||
- [**Development-Only Stability Checks**](#https://reselect.js.org/api/development-only-stability-checks)
|
||||
- **[`lruMemoize`]**
|
||||
- **[`weakMapMemoize`]**
|
||||
- [**FAQ**](#https://reselect.js.org/FAQ)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Reselect exports a [`createSelector`] API, which generates memoized selector functions. [`createSelector`] accepts one or more [input selectors], which extract values from arguments, and a [result function] function that receives the extracted values and should return a derived value. If the generated [output selector] is called multiple times, the output will only be recalculated when the extracted values have changed.
|
||||
|
||||
You can play around with the following **example** in [this CodeSandbox](https://codesandbox.io/s/reselect-example-g3k9gf?file=/src/index.js):
|
||||
|
||||
```ts
|
||||
import { createSelector } from 'reselect'
|
||||
|
||||
interface RootState {
|
||||
todos: { id: number; completed: boolean }[]
|
||||
alerts: { id: number; read: boolean }[]
|
||||
}
|
||||
|
||||
const state: RootState = {
|
||||
todos: [
|
||||
{ id: 0, completed: false },
|
||||
{ id: 1, completed: true }
|
||||
],
|
||||
alerts: [
|
||||
{ id: 0, read: false },
|
||||
{ id: 1, read: true }
|
||||
]
|
||||
}
|
||||
|
||||
const selectCompletedTodos = (state: RootState) => {
|
||||
console.log('selector ran')
|
||||
return state.todos.filter(todo => todo.completed === true)
|
||||
}
|
||||
|
||||
selectCompletedTodos(state) // selector ran
|
||||
selectCompletedTodos(state) // selector ran
|
||||
selectCompletedTodos(state) // selector ran
|
||||
|
||||
const memoizedSelectCompletedTodos = createSelector(
|
||||
[(state: RootState) => state.todos],
|
||||
todos => {
|
||||
console.log('memoized selector ran')
|
||||
return todos.filter(todo => todo.completed === true)
|
||||
}
|
||||
)
|
||||
|
||||
memoizedSelectCompletedTodos(state) // memoized selector ran
|
||||
memoizedSelectCompletedTodos(state)
|
||||
memoizedSelectCompletedTodos(state)
|
||||
|
||||
console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false
|
||||
|
||||
console.log(
|
||||
memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state)
|
||||
) //=> true
|
||||
```
|
||||
|
||||
As you can see from the example above, `memoizedSelectCompletedTodos` does not run the second or third time, but we still get the same return value as last time.
|
||||
|
||||
In addition to skipping unnecessary recalculations, `memoizedSelectCompletedTodos` returns the existing result reference if there is no recalculation. This is important for libraries like [React-Redux] or [React] that often rely on reference equality checks to optimize UI updates.
|
||||
|
||||
---
|
||||
|
||||
## Terminology
|
||||
|
||||
- <a name="selector-function"></a>[**Selector Function**](#selector-function): A function that accepts one or more JavaScript values as arguments, and derives a result. When used with [Redux], the first argument is typically the entire Redux store state.
|
||||
- <a name="input-selectors"></a>[**input selectors**](#input-selectors): Basic selector functions used as building blocks for creating a memoized selector. They are passed as the first argument(s) to [`createSelector`], and are called with all selector arguments. They are responsible for extracting and providing necessary values to the [result function].
|
||||
- <a name="output-selector"></a>[**Output Selector**](#output-selector): The actual memoized selectors created by [`createSelector`].
|
||||
- <a name="result-function"></a>[**Result Function**](#result-function): The function that comes after the [input selectors]. It takes the [input selectors]' return values as arguments and returns a result.
|
||||
- <a name="dependencies"></a>[**`Dependencies`**](#dependencies): Same as [input selectors]. They are what the [output selector] "depends" on.
|
||||
|
||||
The below example serves as a visual aid:
|
||||
|
||||
```ts
|
||||
const outputSelector = createSelector(
|
||||
[inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`.
|
||||
resultFunc // Result function
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What's New in 5.0.0?
|
||||
|
||||
Version 5.0.0 introduces several new features and improvements:
|
||||
|
||||
- **Customization Enhancements**:
|
||||
|
||||
- Added the ability to pass an options object to [`createSelectorCreator`], allowing for customized `memoize` and `argsMemoize` functions, alongside their respective options (`memoizeOptions` and `argsMemoizeOptions`).
|
||||
- The [`createSelector`] function now supports direct customization of `memoize` and `argsMemoize` within its options object.
|
||||
|
||||
- **Memoization Functions**:
|
||||
|
||||
- Introduced new experimental memoization functions: `weakMapMemoize` and `unstable_autotrackMemoize`.
|
||||
- Incorporated `memoize` and `argsMemoize` into the [output selector fields] for debugging purposes.
|
||||
|
||||
- **TypeScript Support and Performance**:
|
||||
|
||||
- Discontinued support for TypeScript versions below 4.7, aligning with modern TypeScript features.
|
||||
- Significantly improved TypeScript performance for nesting [output selectors][output selector]. The nesting limit has increased from approximately 8 to around 30 [output selectors][output selector], greatly reducing the occurrence of the infamous `Type instantiation is excessively deep and possibly infinite` error.
|
||||
|
||||
- **Selector API Enhancements**:
|
||||
|
||||
- Removed the second overload of `createStructuredSelector` due to its susceptibility to runtime errors.
|
||||
|
||||
- **Additional Functionalities**:
|
||||
|
||||
- Added `dependencyRecomputations` and `resetDependencyRecomputations` to the [output selector fields]. These additions provide greater control and insight over [input selectors], complementing the new `argsMemoize` API.
|
||||
- Introduced `inputStabilityCheck`, a development tool that runs the [input selectors] twice using the same arguments and triggers a warning If they return differing results for the same call.
|
||||
- Introduced `identityFunctionCheck`, a development tool that checks to see if the [result function] returns its own input.
|
||||
|
||||
These updates aim to enhance flexibility, performance, and developer experience. For detailed usage and examples, refer to the updated documentation sections for each feature.
|
||||
|
||||
- **Breaking Changes**:
|
||||
|
||||
- Removed `ParametricSelector` and `OutputParametricSelector` types. Their functionalities are now integrated into `Selector` and `OutputSelector` respectively, which inherently support additional parameters.
|
||||
|
||||
<div align="right">[ <a href="installation">↑ Back to top ↑</a> ]</div>
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## References
|
||||
|
||||
<details><summary><b>Click to Expand</b></summary>
|
||||
|
||||
Originally inspired by getters in [NuclearJS](https://github.com/optimizely/nuclear-js.git), [subscriptions](https://github.com/Day8/re-frame#just-a-read-only-cursor) in [re-frame](https://github.com/Day8/re-frame) and this [proposal](https://github.com/reduxjs/redux/pull/169) from [speedskater](https://github.com/speedskater).
|
||||
|
||||
[typescript-badge]: https://img.shields.io/badge/TypeScript-v4%2E7%2B-007ACC?style=for-the-badge&logo=TypeScript&logoColor=black&labelColor=blue&color=gray
|
||||
[build-badge]: https://img.shields.io/github/actions/workflow/status/reduxjs/reselect/build-and-test-types.yml?branch=master&style=for-the-badge
|
||||
[build]: https://github.com/reduxjs/reselect/actions/workflows/build-and-test-types.yml
|
||||
[npm-badge]: https://img.shields.io/npm/v/reselect.svg?style=for-the-badge
|
||||
[npm]: https://www.npmjs.org/package/reselect
|
||||
[coveralls-badge]: https://img.shields.io/coveralls/reduxjs/reselect/master.svg?style=for-the-badge
|
||||
[coveralls]: https://coveralls.io/github/reduxjs/reselect
|
||||
|
||||
<!-- External Links -->
|
||||
|
||||
[Redux]: https://redux.js.org 'Redux'
|
||||
[React]: https://react.dev 'React'
|
||||
[React-Redux]: https://react-redux.js.org 'React-Redux'
|
||||
|
||||
<!-- Internal Links -->
|
||||
|
||||
[selector]: #selector-function 'Selector Function'
|
||||
[input selectors]: #input-selectors 'Input Selectors'
|
||||
[output selector]: #output-selector 'Output Selector'
|
||||
[result function]: #result-function 'Result Function'
|
||||
[output selector fields]: https://reselect.js.org/api/createSelector#output-selector-fields 'Output Selector Fields'
|
||||
[`createSelector`]: https://reselect.js.org/api/createSelector 'createSelector'
|
||||
[`createSelectorCreator`]: https://reselect.js.org/api/createSelectorCreator 'createSelectorCreator'
|
||||
[`lruMemoize`]: https://reselect.js.org/api/lruMemoize 'lruMemoize'
|
||||
[`weakMapMemoize`]: https://reselect.js.org/api/weakMapMemoize 'weakMapMemoize'
|
||||
[`createStructuredSelector`]: https://reselect.js.org/api/createStructuredSelector 'createStructuredSelector'
|
||||
|
||||
</details>
|
||||
777
node_modules/reselect/dist/cjs/reselect.cjs
generated
vendored
Normal file
777
node_modules/reselect/dist/cjs/reselect.cjs
generated
vendored
Normal file
|
|
@ -0,0 +1,777 @@
|
|||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/index.ts
|
||||
var src_exports = {};
|
||||
__export(src_exports, {
|
||||
createSelector: () => createSelector,
|
||||
createSelectorCreator: () => createSelectorCreator,
|
||||
createStructuredSelector: () => createStructuredSelector,
|
||||
lruMemoize: () => lruMemoize,
|
||||
referenceEqualityCheck: () => referenceEqualityCheck,
|
||||
setGlobalDevModeChecks: () => setGlobalDevModeChecks,
|
||||
unstable_autotrackMemoize: () => autotrackMemoize,
|
||||
weakMapMemoize: () => weakMapMemoize
|
||||
});
|
||||
module.exports = __toCommonJS(src_exports);
|
||||
|
||||
// src/devModeChecks/identityFunctionCheck.ts
|
||||
var runIdentityFunctionCheck = (resultFunc, inputSelectorsResults, outputSelectorResult) => {
|
||||
if (inputSelectorsResults.length === 1 && inputSelectorsResults[0] === outputSelectorResult) {
|
||||
let isInputSameAsOutput = false;
|
||||
try {
|
||||
const emptyObject = {};
|
||||
if (resultFunc(emptyObject) === emptyObject)
|
||||
isInputSameAsOutput = true;
|
||||
} catch {
|
||||
}
|
||||
if (isInputSameAsOutput) {
|
||||
let stack = void 0;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
;
|
||||
({ stack } = e);
|
||||
}
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// src/devModeChecks/inputStabilityCheck.ts
|
||||
var runInputStabilityCheck = (inputSelectorResultsObject, options, inputSelectorArgs) => {
|
||||
const { memoize, memoizeOptions } = options;
|
||||
const { inputSelectorResults, inputSelectorResultsCopy } = inputSelectorResultsObject;
|
||||
const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions);
|
||||
const areInputSelectorResultsEqual = createAnEmptyObject.apply(null, inputSelectorResults) === createAnEmptyObject.apply(null, inputSelectorResultsCopy);
|
||||
if (!areInputSelectorResultsEqual) {
|
||||
let stack = void 0;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
;
|
||||
({ stack } = e);
|
||||
}
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// src/devModeChecks/setGlobalDevModeChecks.ts
|
||||
var globalDevModeChecks = {
|
||||
inputStabilityCheck: "once",
|
||||
identityFunctionCheck: "once"
|
||||
};
|
||||
var setGlobalDevModeChecks = (devModeChecks) => {
|
||||
Object.assign(globalDevModeChecks, devModeChecks);
|
||||
};
|
||||
|
||||
// src/utils.ts
|
||||
var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
|
||||
function assertIsFunction(func, errorMessage = `expected a function, instead received ${typeof func}`) {
|
||||
if (typeof func !== "function") {
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
}
|
||||
function assertIsObject(object, errorMessage = `expected an object, instead received ${typeof object}`) {
|
||||
if (typeof object !== "object") {
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
}
|
||||
function assertIsArrayOfFunctions(array, errorMessage = `expected all items to be functions, instead received the following types: `) {
|
||||
if (!array.every((item) => typeof item === "function")) {
|
||||
const itemTypes = array.map(
|
||||
(item) => typeof item === "function" ? `function ${item.name || "unnamed"}()` : typeof item
|
||||
).join(", ");
|
||||
throw new TypeError(`${errorMessage}[${itemTypes}]`);
|
||||
}
|
||||
}
|
||||
var ensureIsArray = (item) => {
|
||||
return Array.isArray(item) ? item : [item];
|
||||
};
|
||||
function getDependencies(createSelectorArgs) {
|
||||
const dependencies = Array.isArray(createSelectorArgs[0]) ? createSelectorArgs[0] : createSelectorArgs;
|
||||
assertIsArrayOfFunctions(
|
||||
dependencies,
|
||||
`createSelector expects all input-selectors to be functions, but received the following types: `
|
||||
);
|
||||
return dependencies;
|
||||
}
|
||||
function collectInputSelectorResults(dependencies, inputSelectorArgs) {
|
||||
const inputSelectorResults = [];
|
||||
const { length } = dependencies;
|
||||
for (let i = 0; i < length; i++) {
|
||||
inputSelectorResults.push(dependencies[i].apply(null, inputSelectorArgs));
|
||||
}
|
||||
return inputSelectorResults;
|
||||
}
|
||||
var getDevModeChecksExecutionInfo = (firstRun, 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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// src/autotrackMemoize/autotracking.ts
|
||||
var $REVISION = 0;
|
||||
var CURRENT_TRACKER = null;
|
||||
var Cell = class {
|
||||
revision = $REVISION;
|
||||
_value;
|
||||
_lastValue;
|
||||
_isEqual = tripleEq;
|
||||
constructor(initialValue, isEqual = 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, b) {
|
||||
return a === b;
|
||||
}
|
||||
var TrackingCache = class {
|
||||
_cachedValue;
|
||||
_cachedRevision = -1;
|
||||
_deps = [];
|
||||
hits = 0;
|
||||
fn;
|
||||
constructor(fn) {
|
||||
this.fn = fn;
|
||||
}
|
||||
clear() {
|
||||
this._cachedValue = void 0;
|
||||
this._cachedRevision = -1;
|
||||
this._deps = [];
|
||||
this.hits = 0;
|
||||
}
|
||||
get value() {
|
||||
if (this.revision > this._cachedRevision) {
|
||||
const { fn } = this;
|
||||
const currentTracker = /* @__PURE__ */ new Set();
|
||||
const prevTracker = CURRENT_TRACKER;
|
||||
CURRENT_TRACKER = currentTracker;
|
||||
this._cachedValue = fn();
|
||||
CURRENT_TRACKER = prevTracker;
|
||||
this.hits++;
|
||||
this._deps = Array.from(currentTracker);
|
||||
this._cachedRevision = this.revision;
|
||||
}
|
||||
CURRENT_TRACKER?.add(this);
|
||||
return this._cachedValue;
|
||||
}
|
||||
get revision() {
|
||||
return Math.max(...this._deps.map((d) => d.revision), 0);
|
||||
}
|
||||
};
|
||||
function getValue(cell) {
|
||||
if (!(cell instanceof Cell)) {
|
||||
console.warn("Not a valid cell! ", cell);
|
||||
}
|
||||
return cell.value;
|
||||
}
|
||||
function setValue(storage, value) {
|
||||
if (!(storage instanceof Cell)) {
|
||||
throw new TypeError(
|
||||
"setValue must be passed a tracked store created with `createStorage`."
|
||||
);
|
||||
}
|
||||
storage.value = storage._lastValue = value;
|
||||
}
|
||||
function createCell(initialValue, isEqual = tripleEq) {
|
||||
return new Cell(initialValue, isEqual);
|
||||
}
|
||||
function createCache(fn) {
|
||||
assertIsFunction(
|
||||
fn,
|
||||
"the first parameter to `createCache` must be a function"
|
||||
);
|
||||
return new TrackingCache(fn);
|
||||
}
|
||||
|
||||
// src/autotrackMemoize/tracking.ts
|
||||
var neverEq = (a, b) => false;
|
||||
function createTag() {
|
||||
return createCell(null, neverEq);
|
||||
}
|
||||
function dirtyTag(tag, value) {
|
||||
setValue(tag, value);
|
||||
}
|
||||
var consumeCollection = (node) => {
|
||||
let tag = node.collectionTag;
|
||||
if (tag === null) {
|
||||
tag = node.collectionTag = createTag();
|
||||
}
|
||||
getValue(tag);
|
||||
};
|
||||
var dirtyCollection = (node) => {
|
||||
const tag = node.collectionTag;
|
||||
if (tag !== null) {
|
||||
dirtyTag(tag, null);
|
||||
}
|
||||
};
|
||||
|
||||
// src/autotrackMemoize/proxy.ts
|
||||
var REDUX_PROXY_LABEL = Symbol();
|
||||
var nextId = 0;
|
||||
var proto = Object.getPrototypeOf({});
|
||||
var ObjectTreeNode = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.value = value;
|
||||
this.tag.value = value;
|
||||
}
|
||||
proxy = new Proxy(this, objectProxyHandler);
|
||||
tag = createTag();
|
||||
tags = {};
|
||||
children = {};
|
||||
collectionTag = null;
|
||||
id = nextId++;
|
||||
};
|
||||
var objectProxyHandler = {
|
||||
get(node, key) {
|
||||
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 === void 0) {
|
||||
childNode = node.children[key] = createNode(childValue);
|
||||
}
|
||||
if (childNode.tag) {
|
||||
getValue(childNode.tag);
|
||||
}
|
||||
return childNode.proxy;
|
||||
} else {
|
||||
let tag = node.tags[key];
|
||||
if (tag === void 0) {
|
||||
tag = node.tags[key] = createTag();
|
||||
tag.value = childValue;
|
||||
}
|
||||
getValue(tag);
|
||||
return childValue;
|
||||
}
|
||||
}
|
||||
const res = calculateResult();
|
||||
return res;
|
||||
},
|
||||
ownKeys(node) {
|
||||
consumeCollection(node);
|
||||
return Reflect.ownKeys(node.value);
|
||||
},
|
||||
getOwnPropertyDescriptor(node, prop) {
|
||||
return Reflect.getOwnPropertyDescriptor(node.value, prop);
|
||||
},
|
||||
has(node, prop) {
|
||||
return Reflect.has(node.value, prop);
|
||||
}
|
||||
};
|
||||
var ArrayTreeNode = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.value = value;
|
||||
this.tag.value = value;
|
||||
}
|
||||
proxy = new Proxy([this], arrayProxyHandler);
|
||||
tag = createTag();
|
||||
tags = {};
|
||||
children = {};
|
||||
collectionTag = null;
|
||||
id = nextId++;
|
||||
};
|
||||
var arrayProxyHandler = {
|
||||
get([node], key) {
|
||||
if (key === "length") {
|
||||
consumeCollection(node);
|
||||
}
|
||||
return objectProxyHandler.get(node, key);
|
||||
},
|
||||
ownKeys([node]) {
|
||||
return objectProxyHandler.ownKeys(node);
|
||||
},
|
||||
getOwnPropertyDescriptor([node], prop) {
|
||||
return objectProxyHandler.getOwnPropertyDescriptor(node, prop);
|
||||
},
|
||||
has([node], prop) {
|
||||
return objectProxyHandler.has(node, prop);
|
||||
}
|
||||
};
|
||||
function createNode(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return new ArrayTreeNode(value);
|
||||
}
|
||||
return new ObjectTreeNode(value);
|
||||
}
|
||||
function updateNode(node, newValue) {
|
||||
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[key];
|
||||
const newChildValue = newValue[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[key];
|
||||
const childValue = childNode.value;
|
||||
if (childValue === newChildValue) {
|
||||
continue;
|
||||
} else if (typeof newChildValue === "object" && newChildValue !== null) {
|
||||
updateNode(childNode, newChildValue);
|
||||
} else {
|
||||
deleteNode(childNode);
|
||||
delete children[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
function deleteNode(node) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// src/lruMemoize.ts
|
||||
function createSingletonCache(equals) {
|
||||
let entry;
|
||||
return {
|
||||
get(key) {
|
||||
if (entry && equals(entry.key, key)) {
|
||||
return entry.value;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
},
|
||||
put(key, value) {
|
||||
entry = { key, value };
|
||||
},
|
||||
getEntries() {
|
||||
return entry ? [entry] : [];
|
||||
},
|
||||
clear() {
|
||||
entry = void 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
function createLruCache(maxSize, equals) {
|
||||
let entries = [];
|
||||
function get(key) {
|
||||
const cacheIndex = entries.findIndex((entry) => equals(key, entry.key));
|
||||
if (cacheIndex > -1) {
|
||||
const entry = entries[cacheIndex];
|
||||
if (cacheIndex > 0) {
|
||||
entries.splice(cacheIndex, 1);
|
||||
entries.unshift(entry);
|
||||
}
|
||||
return entry.value;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
function put(key, value) {
|
||||
if (get(key) === NOT_FOUND) {
|
||||
entries.unshift({ key, value });
|
||||
if (entries.length > maxSize) {
|
||||
entries.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
function getEntries() {
|
||||
return entries;
|
||||
}
|
||||
function clear() {
|
||||
entries = [];
|
||||
}
|
||||
return { get, put, getEntries, clear };
|
||||
}
|
||||
var referenceEqualityCheck = (a, b) => a === b;
|
||||
function createCacheKeyComparator(equalityCheck) {
|
||||
return function areArgumentsShallowlyEqual(prev, next) {
|
||||
if (prev === null || next === null || prev.length !== next.length) {
|
||||
return false;
|
||||
}
|
||||
const { length } = prev;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (!equalityCheck(prev[i], next[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function lruMemoize(func, equalityCheckOrOptions) {
|
||||
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);
|
||||
if (value === NOT_FOUND) {
|
||||
value = func.apply(null, arguments);
|
||||
resultsCount++;
|
||||
if (resultEqualityCheck) {
|
||||
const entries = cache.getEntries();
|
||||
const matchingEntry = entries.find(
|
||||
(entry) => resultEqualityCheck(entry.value, value)
|
||||
);
|
||||
if (matchingEntry) {
|
||||
value = matchingEntry.value;
|
||||
resultsCount !== 0 && resultsCount--;
|
||||
}
|
||||
}
|
||||
cache.put(arguments, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
memoized.clearCache = () => {
|
||||
cache.clear();
|
||||
memoized.resetResultsCount();
|
||||
};
|
||||
memoized.resultsCount = () => resultsCount;
|
||||
memoized.resetResultsCount = () => {
|
||||
resultsCount = 0;
|
||||
};
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// src/autotrackMemoize/autotrackMemoize.ts
|
||||
function autotrackMemoize(func) {
|
||||
const node = createNode(
|
||||
[]
|
||||
);
|
||||
let lastArgs = null;
|
||||
const shallowEqual = createCacheKeyComparator(referenceEqualityCheck);
|
||||
const cache = createCache(() => {
|
||||
const res = func.apply(null, node.proxy);
|
||||
return res;
|
||||
});
|
||||
function memoized() {
|
||||
if (!shallowEqual(lastArgs, arguments)) {
|
||||
updateNode(node, arguments);
|
||||
lastArgs = arguments;
|
||||
}
|
||||
return cache.value;
|
||||
}
|
||||
memoized.clearCache = () => {
|
||||
return cache.clear();
|
||||
};
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// src/weakMapMemoize.ts
|
||||
var StrongRef = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
}
|
||||
deref() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
var Ref = typeof WeakRef !== "undefined" ? WeakRef : StrongRef;
|
||||
var UNTERMINATED = 0;
|
||||
var TERMINATED = 1;
|
||||
function createCacheNode() {
|
||||
return {
|
||||
s: UNTERMINATED,
|
||||
v: void 0,
|
||||
o: null,
|
||||
p: null
|
||||
};
|
||||
}
|
||||
function weakMapMemoize(func, options = {}) {
|
||||
let fnNode = createCacheNode();
|
||||
const { resultEqualityCheck } = options;
|
||||
let lastResult;
|
||||
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) {
|
||||
let objectCache = cacheNode.o;
|
||||
if (objectCache === null) {
|
||||
cacheNode.o = objectCache = /* @__PURE__ */ new WeakMap();
|
||||
}
|
||||
const objectNode = objectCache.get(arg);
|
||||
if (objectNode === void 0) {
|
||||
cacheNode = createCacheNode();
|
||||
objectCache.set(arg, cacheNode);
|
||||
} else {
|
||||
cacheNode = objectNode;
|
||||
}
|
||||
} else {
|
||||
let primitiveCache = cacheNode.p;
|
||||
if (primitiveCache === null) {
|
||||
cacheNode.p = primitiveCache = /* @__PURE__ */ new Map();
|
||||
}
|
||||
const primitiveNode = primitiveCache.get(arg);
|
||||
if (primitiveNode === void 0) {
|
||||
cacheNode = createCacheNode();
|
||||
primitiveCache.set(arg, cacheNode);
|
||||
} else {
|
||||
cacheNode = primitiveNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
const terminatedNode = cacheNode;
|
||||
let result;
|
||||
if (cacheNode.s === TERMINATED) {
|
||||
result = cacheNode.v;
|
||||
} else {
|
||||
result = func.apply(null, arguments);
|
||||
resultsCount++;
|
||||
if (resultEqualityCheck) {
|
||||
const lastResultValue = lastResult?.deref?.() ?? lastResult;
|
||||
if (lastResultValue != null && resultEqualityCheck(lastResultValue, 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;
|
||||
}
|
||||
|
||||
// src/createSelectorCreator.ts
|
||||
function createSelectorCreator(memoizeOrOptions, ...memoizeOptionsFromArgs) {
|
||||
const createSelectorCreatorOptions = typeof memoizeOrOptions === "function" ? {
|
||||
memoize: memoizeOrOptions,
|
||||
memoizeOptions: memoizeOptionsFromArgs
|
||||
} : memoizeOrOptions;
|
||||
const createSelector2 = (...createSelectorArgs) => {
|
||||
let recomputations = 0;
|
||||
let dependencyRecomputations = 0;
|
||||
let lastResult;
|
||||
let directlyPassedOptions = {};
|
||||
let resultFunc = createSelectorArgs.pop();
|
||||
if (typeof resultFunc === "object") {
|
||||
directlyPassedOptions = resultFunc;
|
||||
resultFunc = createSelectorArgs.pop();
|
||||
}
|
||||
assertIsFunction(
|
||||
resultFunc,
|
||||
`createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`
|
||||
);
|
||||
const combinedOptions = {
|
||||
...createSelectorCreatorOptions,
|
||||
...directlyPassedOptions
|
||||
};
|
||||
const {
|
||||
memoize,
|
||||
memoizeOptions = [],
|
||||
argsMemoize = weakMapMemoize,
|
||||
argsMemoizeOptions = [],
|
||||
devModeChecks = {}
|
||||
} = combinedOptions;
|
||||
const finalMemoizeOptions = ensureIsArray(memoizeOptions);
|
||||
const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions);
|
||||
const dependencies = getDependencies(createSelectorArgs);
|
||||
const memoizedResultFunc = memoize(function recomputationWrapper() {
|
||||
recomputations++;
|
||||
return resultFunc.apply(
|
||||
null,
|
||||
arguments
|
||||
);
|
||||
}, ...finalMemoizeOptions);
|
||||
let firstRun = true;
|
||||
const selector = argsMemoize(function dependenciesChecker() {
|
||||
dependencyRecomputations++;
|
||||
const inputSelectorResults = collectInputSelectorResults(
|
||||
dependencies,
|
||||
arguments
|
||||
);
|
||||
lastResult = memoizedResultFunc.apply(null, inputSelectorResults);
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
const { identityFunctionCheck, inputStabilityCheck } = getDevModeChecksExecutionInfo(firstRun, devModeChecks);
|
||||
if (identityFunctionCheck.shouldRun) {
|
||||
identityFunctionCheck.run(
|
||||
resultFunc,
|
||||
inputSelectorResults,
|
||||
lastResult
|
||||
);
|
||||
}
|
||||
if (inputStabilityCheck.shouldRun) {
|
||||
const inputSelectorResultsCopy = collectInputSelectorResults(
|
||||
dependencies,
|
||||
arguments
|
||||
);
|
||||
inputStabilityCheck.run(
|
||||
{ inputSelectorResults, inputSelectorResultsCopy },
|
||||
{ memoize, memoizeOptions: finalMemoizeOptions },
|
||||
arguments
|
||||
);
|
||||
}
|
||||
if (firstRun)
|
||||
firstRun = false;
|
||||
}
|
||||
return lastResult;
|
||||
}, ...finalArgsMemoizeOptions);
|
||||
return Object.assign(selector, {
|
||||
resultFunc,
|
||||
memoizedResultFunc,
|
||||
dependencies,
|
||||
dependencyRecomputations: () => dependencyRecomputations,
|
||||
resetDependencyRecomputations: () => {
|
||||
dependencyRecomputations = 0;
|
||||
},
|
||||
lastResult: () => lastResult,
|
||||
recomputations: () => recomputations,
|
||||
resetRecomputations: () => {
|
||||
recomputations = 0;
|
||||
},
|
||||
memoize,
|
||||
argsMemoize
|
||||
});
|
||||
};
|
||||
Object.assign(createSelector2, {
|
||||
withTypes: () => createSelector2
|
||||
});
|
||||
return createSelector2;
|
||||
}
|
||||
var createSelector = /* @__PURE__ */ createSelectorCreator(weakMapMemoize);
|
||||
|
||||
// src/createStructuredSelector.ts
|
||||
var createStructuredSelector = Object.assign(
|
||||
(inputSelectorsObject, selectorCreator = createSelector) => {
|
||||
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) => {
|
||||
return inputSelectorResults.reduce((composition, value, index) => {
|
||||
composition[inputSelectorKeys[index]] = value;
|
||||
return composition;
|
||||
}, {});
|
||||
}
|
||||
);
|
||||
return structuredSelector;
|
||||
},
|
||||
{ withTypes: () => createStructuredSelector }
|
||||
);
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
createSelector,
|
||||
createSelectorCreator,
|
||||
createStructuredSelector,
|
||||
lruMemoize,
|
||||
referenceEqualityCheck,
|
||||
setGlobalDevModeChecks,
|
||||
unstable_autotrackMemoize,
|
||||
weakMapMemoize
|
||||
});
|
||||
//# sourceMappingURL=reselect.cjs.map
|
||||
1
node_modules/reselect/dist/cjs/reselect.cjs.map
generated
vendored
Normal file
1
node_modules/reselect/dist/cjs/reselect.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
node_modules/reselect/dist/reselect.browser.mjs
generated
vendored
Normal file
2
node_modules/reselect/dist/reselect.browser.mjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/reselect/dist/reselect.browser.mjs.map
generated
vendored
Normal file
1
node_modules/reselect/dist/reselect.browser.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1429
node_modules/reselect/dist/reselect.d.ts
generated
vendored
Normal file
1429
node_modules/reselect/dist/reselect.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
759
node_modules/reselect/dist/reselect.legacy-esm.js
generated
vendored
Normal file
759
node_modules/reselect/dist/reselect.legacy-esm.js
generated
vendored
Normal file
|
|
@ -0,0 +1,759 @@
|
|||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __spreadValues = (a, b) => {
|
||||
for (var prop in b || (b = {}))
|
||||
if (__hasOwnProp.call(b, prop))
|
||||
__defNormalProp(a, prop, b[prop]);
|
||||
if (__getOwnPropSymbols)
|
||||
for (var prop of __getOwnPropSymbols(b)) {
|
||||
if (__propIsEnum.call(b, prop))
|
||||
__defNormalProp(a, prop, b[prop]);
|
||||
}
|
||||
return a;
|
||||
};
|
||||
var __publicField = (obj, key, value) => {
|
||||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
return value;
|
||||
};
|
||||
|
||||
// src/devModeChecks/identityFunctionCheck.ts
|
||||
var runIdentityFunctionCheck = (resultFunc, inputSelectorsResults, outputSelectorResult) => {
|
||||
if (inputSelectorsResults.length === 1 && inputSelectorsResults[0] === outputSelectorResult) {
|
||||
let isInputSameAsOutput = false;
|
||||
try {
|
||||
const emptyObject = {};
|
||||
if (resultFunc(emptyObject) === emptyObject)
|
||||
isInputSameAsOutput = true;
|
||||
} catch (e) {
|
||||
}
|
||||
if (isInputSameAsOutput) {
|
||||
let stack = void 0;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
;
|
||||
({ stack } = e);
|
||||
}
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// src/devModeChecks/inputStabilityCheck.ts
|
||||
var runInputStabilityCheck = (inputSelectorResultsObject, options, inputSelectorArgs) => {
|
||||
const { memoize, memoizeOptions } = options;
|
||||
const { inputSelectorResults, inputSelectorResultsCopy } = inputSelectorResultsObject;
|
||||
const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions);
|
||||
const areInputSelectorResultsEqual = createAnEmptyObject.apply(null, inputSelectorResults) === createAnEmptyObject.apply(null, inputSelectorResultsCopy);
|
||||
if (!areInputSelectorResultsEqual) {
|
||||
let stack = void 0;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
;
|
||||
({ stack } = e);
|
||||
}
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// src/devModeChecks/setGlobalDevModeChecks.ts
|
||||
var globalDevModeChecks = {
|
||||
inputStabilityCheck: "once",
|
||||
identityFunctionCheck: "once"
|
||||
};
|
||||
var setGlobalDevModeChecks = (devModeChecks) => {
|
||||
Object.assign(globalDevModeChecks, devModeChecks);
|
||||
};
|
||||
|
||||
// src/utils.ts
|
||||
var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
|
||||
function assertIsFunction(func, errorMessage = `expected a function, instead received ${typeof func}`) {
|
||||
if (typeof func !== "function") {
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
}
|
||||
function assertIsObject(object, errorMessage = `expected an object, instead received ${typeof object}`) {
|
||||
if (typeof object !== "object") {
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
}
|
||||
function assertIsArrayOfFunctions(array, errorMessage = `expected all items to be functions, instead received the following types: `) {
|
||||
if (!array.every((item) => typeof item === "function")) {
|
||||
const itemTypes = array.map(
|
||||
(item) => typeof item === "function" ? `function ${item.name || "unnamed"}()` : typeof item
|
||||
).join(", ");
|
||||
throw new TypeError(`${errorMessage}[${itemTypes}]`);
|
||||
}
|
||||
}
|
||||
var ensureIsArray = (item) => {
|
||||
return Array.isArray(item) ? item : [item];
|
||||
};
|
||||
function getDependencies(createSelectorArgs) {
|
||||
const dependencies = Array.isArray(createSelectorArgs[0]) ? createSelectorArgs[0] : createSelectorArgs;
|
||||
assertIsArrayOfFunctions(
|
||||
dependencies,
|
||||
`createSelector expects all input-selectors to be functions, but received the following types: `
|
||||
);
|
||||
return dependencies;
|
||||
}
|
||||
function collectInputSelectorResults(dependencies, inputSelectorArgs) {
|
||||
const inputSelectorResults = [];
|
||||
const { length } = dependencies;
|
||||
for (let i = 0; i < length; i++) {
|
||||
inputSelectorResults.push(dependencies[i].apply(null, inputSelectorArgs));
|
||||
}
|
||||
return inputSelectorResults;
|
||||
}
|
||||
var getDevModeChecksExecutionInfo = (firstRun, devModeChecks) => {
|
||||
const { identityFunctionCheck, inputStabilityCheck } = __spreadValues(__spreadValues({}, globalDevModeChecks), devModeChecks);
|
||||
return {
|
||||
identityFunctionCheck: {
|
||||
shouldRun: identityFunctionCheck === "always" || identityFunctionCheck === "once" && firstRun,
|
||||
run: runIdentityFunctionCheck
|
||||
},
|
||||
inputStabilityCheck: {
|
||||
shouldRun: inputStabilityCheck === "always" || inputStabilityCheck === "once" && firstRun,
|
||||
run: runInputStabilityCheck
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// src/autotrackMemoize/autotracking.ts
|
||||
var $REVISION = 0;
|
||||
var CURRENT_TRACKER = null;
|
||||
var Cell = class {
|
||||
constructor(initialValue, isEqual = tripleEq) {
|
||||
__publicField(this, "revision", $REVISION);
|
||||
__publicField(this, "_value");
|
||||
__publicField(this, "_lastValue");
|
||||
__publicField(this, "_isEqual", 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 == null ? void 0 : 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, b) {
|
||||
return a === b;
|
||||
}
|
||||
var TrackingCache = class {
|
||||
constructor(fn) {
|
||||
__publicField(this, "_cachedValue");
|
||||
__publicField(this, "_cachedRevision", -1);
|
||||
__publicField(this, "_deps", []);
|
||||
__publicField(this, "hits", 0);
|
||||
__publicField(this, "fn");
|
||||
this.fn = fn;
|
||||
}
|
||||
clear() {
|
||||
this._cachedValue = void 0;
|
||||
this._cachedRevision = -1;
|
||||
this._deps = [];
|
||||
this.hits = 0;
|
||||
}
|
||||
get value() {
|
||||
if (this.revision > this._cachedRevision) {
|
||||
const { fn } = this;
|
||||
const currentTracker = /* @__PURE__ */ new Set();
|
||||
const prevTracker = CURRENT_TRACKER;
|
||||
CURRENT_TRACKER = currentTracker;
|
||||
this._cachedValue = fn();
|
||||
CURRENT_TRACKER = prevTracker;
|
||||
this.hits++;
|
||||
this._deps = Array.from(currentTracker);
|
||||
this._cachedRevision = this.revision;
|
||||
}
|
||||
CURRENT_TRACKER == null ? void 0 : CURRENT_TRACKER.add(this);
|
||||
return this._cachedValue;
|
||||
}
|
||||
get revision() {
|
||||
return Math.max(...this._deps.map((d) => d.revision), 0);
|
||||
}
|
||||
};
|
||||
function getValue(cell) {
|
||||
if (!(cell instanceof Cell)) {
|
||||
console.warn("Not a valid cell! ", cell);
|
||||
}
|
||||
return cell.value;
|
||||
}
|
||||
function setValue(storage, value) {
|
||||
if (!(storage instanceof Cell)) {
|
||||
throw new TypeError(
|
||||
"setValue must be passed a tracked store created with `createStorage`."
|
||||
);
|
||||
}
|
||||
storage.value = storage._lastValue = value;
|
||||
}
|
||||
function createCell(initialValue, isEqual = tripleEq) {
|
||||
return new Cell(initialValue, isEqual);
|
||||
}
|
||||
function createCache(fn) {
|
||||
assertIsFunction(
|
||||
fn,
|
||||
"the first parameter to `createCache` must be a function"
|
||||
);
|
||||
return new TrackingCache(fn);
|
||||
}
|
||||
|
||||
// src/autotrackMemoize/tracking.ts
|
||||
var neverEq = (a, b) => false;
|
||||
function createTag() {
|
||||
return createCell(null, neverEq);
|
||||
}
|
||||
function dirtyTag(tag, value) {
|
||||
setValue(tag, value);
|
||||
}
|
||||
var consumeCollection = (node) => {
|
||||
let tag = node.collectionTag;
|
||||
if (tag === null) {
|
||||
tag = node.collectionTag = createTag();
|
||||
}
|
||||
getValue(tag);
|
||||
};
|
||||
var dirtyCollection = (node) => {
|
||||
const tag = node.collectionTag;
|
||||
if (tag !== null) {
|
||||
dirtyTag(tag, null);
|
||||
}
|
||||
};
|
||||
|
||||
// src/autotrackMemoize/proxy.ts
|
||||
var REDUX_PROXY_LABEL = Symbol();
|
||||
var nextId = 0;
|
||||
var proto = Object.getPrototypeOf({});
|
||||
var ObjectTreeNode = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
__publicField(this, "proxy", new Proxy(this, objectProxyHandler));
|
||||
__publicField(this, "tag", createTag());
|
||||
__publicField(this, "tags", {});
|
||||
__publicField(this, "children", {});
|
||||
__publicField(this, "collectionTag", null);
|
||||
__publicField(this, "id", nextId++);
|
||||
this.value = value;
|
||||
this.tag.value = value;
|
||||
}
|
||||
};
|
||||
var objectProxyHandler = {
|
||||
get(node, key) {
|
||||
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 === void 0) {
|
||||
childNode = node.children[key] = createNode(childValue);
|
||||
}
|
||||
if (childNode.tag) {
|
||||
getValue(childNode.tag);
|
||||
}
|
||||
return childNode.proxy;
|
||||
} else {
|
||||
let tag = node.tags[key];
|
||||
if (tag === void 0) {
|
||||
tag = node.tags[key] = createTag();
|
||||
tag.value = childValue;
|
||||
}
|
||||
getValue(tag);
|
||||
return childValue;
|
||||
}
|
||||
}
|
||||
const res = calculateResult();
|
||||
return res;
|
||||
},
|
||||
ownKeys(node) {
|
||||
consumeCollection(node);
|
||||
return Reflect.ownKeys(node.value);
|
||||
},
|
||||
getOwnPropertyDescriptor(node, prop) {
|
||||
return Reflect.getOwnPropertyDescriptor(node.value, prop);
|
||||
},
|
||||
has(node, prop) {
|
||||
return Reflect.has(node.value, prop);
|
||||
}
|
||||
};
|
||||
var ArrayTreeNode = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
__publicField(this, "proxy", new Proxy([this], arrayProxyHandler));
|
||||
__publicField(this, "tag", createTag());
|
||||
__publicField(this, "tags", {});
|
||||
__publicField(this, "children", {});
|
||||
__publicField(this, "collectionTag", null);
|
||||
__publicField(this, "id", nextId++);
|
||||
this.value = value;
|
||||
this.tag.value = value;
|
||||
}
|
||||
};
|
||||
var arrayProxyHandler = {
|
||||
get([node], key) {
|
||||
if (key === "length") {
|
||||
consumeCollection(node);
|
||||
}
|
||||
return objectProxyHandler.get(node, key);
|
||||
},
|
||||
ownKeys([node]) {
|
||||
return objectProxyHandler.ownKeys(node);
|
||||
},
|
||||
getOwnPropertyDescriptor([node], prop) {
|
||||
return objectProxyHandler.getOwnPropertyDescriptor(node, prop);
|
||||
},
|
||||
has([node], prop) {
|
||||
return objectProxyHandler.has(node, prop);
|
||||
}
|
||||
};
|
||||
function createNode(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return new ArrayTreeNode(value);
|
||||
}
|
||||
return new ObjectTreeNode(value);
|
||||
}
|
||||
function updateNode(node, newValue) {
|
||||
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[key];
|
||||
const newChildValue = newValue[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[key];
|
||||
const childValue = childNode.value;
|
||||
if (childValue === newChildValue) {
|
||||
continue;
|
||||
} else if (typeof newChildValue === "object" && newChildValue !== null) {
|
||||
updateNode(childNode, newChildValue);
|
||||
} else {
|
||||
deleteNode(childNode);
|
||||
delete children[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
function deleteNode(node) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// src/lruMemoize.ts
|
||||
function createSingletonCache(equals) {
|
||||
let entry;
|
||||
return {
|
||||
get(key) {
|
||||
if (entry && equals(entry.key, key)) {
|
||||
return entry.value;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
},
|
||||
put(key, value) {
|
||||
entry = { key, value };
|
||||
},
|
||||
getEntries() {
|
||||
return entry ? [entry] : [];
|
||||
},
|
||||
clear() {
|
||||
entry = void 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
function createLruCache(maxSize, equals) {
|
||||
let entries = [];
|
||||
function get(key) {
|
||||
const cacheIndex = entries.findIndex((entry) => equals(key, entry.key));
|
||||
if (cacheIndex > -1) {
|
||||
const entry = entries[cacheIndex];
|
||||
if (cacheIndex > 0) {
|
||||
entries.splice(cacheIndex, 1);
|
||||
entries.unshift(entry);
|
||||
}
|
||||
return entry.value;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
function put(key, value) {
|
||||
if (get(key) === NOT_FOUND) {
|
||||
entries.unshift({ key, value });
|
||||
if (entries.length > maxSize) {
|
||||
entries.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
function getEntries() {
|
||||
return entries;
|
||||
}
|
||||
function clear() {
|
||||
entries = [];
|
||||
}
|
||||
return { get, put, getEntries, clear };
|
||||
}
|
||||
var referenceEqualityCheck = (a, b) => a === b;
|
||||
function createCacheKeyComparator(equalityCheck) {
|
||||
return function areArgumentsShallowlyEqual(prev, next) {
|
||||
if (prev === null || next === null || prev.length !== next.length) {
|
||||
return false;
|
||||
}
|
||||
const { length } = prev;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (!equalityCheck(prev[i], next[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function lruMemoize(func, equalityCheckOrOptions) {
|
||||
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);
|
||||
if (value === NOT_FOUND) {
|
||||
value = func.apply(null, arguments);
|
||||
resultsCount++;
|
||||
if (resultEqualityCheck) {
|
||||
const entries = cache.getEntries();
|
||||
const matchingEntry = entries.find(
|
||||
(entry) => resultEqualityCheck(entry.value, value)
|
||||
);
|
||||
if (matchingEntry) {
|
||||
value = matchingEntry.value;
|
||||
resultsCount !== 0 && resultsCount--;
|
||||
}
|
||||
}
|
||||
cache.put(arguments, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
memoized.clearCache = () => {
|
||||
cache.clear();
|
||||
memoized.resetResultsCount();
|
||||
};
|
||||
memoized.resultsCount = () => resultsCount;
|
||||
memoized.resetResultsCount = () => {
|
||||
resultsCount = 0;
|
||||
};
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// src/autotrackMemoize/autotrackMemoize.ts
|
||||
function autotrackMemoize(func) {
|
||||
const node = createNode(
|
||||
[]
|
||||
);
|
||||
let lastArgs = null;
|
||||
const shallowEqual = createCacheKeyComparator(referenceEqualityCheck);
|
||||
const cache = createCache(() => {
|
||||
const res = func.apply(null, node.proxy);
|
||||
return res;
|
||||
});
|
||||
function memoized() {
|
||||
if (!shallowEqual(lastArgs, arguments)) {
|
||||
updateNode(node, arguments);
|
||||
lastArgs = arguments;
|
||||
}
|
||||
return cache.value;
|
||||
}
|
||||
memoized.clearCache = () => {
|
||||
return cache.clear();
|
||||
};
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// src/weakMapMemoize.ts
|
||||
var StrongRef = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
}
|
||||
deref() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
var Ref = typeof WeakRef !== "undefined" ? WeakRef : StrongRef;
|
||||
var UNTERMINATED = 0;
|
||||
var TERMINATED = 1;
|
||||
function createCacheNode() {
|
||||
return {
|
||||
s: UNTERMINATED,
|
||||
v: void 0,
|
||||
o: null,
|
||||
p: null
|
||||
};
|
||||
}
|
||||
function weakMapMemoize(func, options = {}) {
|
||||
let fnNode = createCacheNode();
|
||||
const { resultEqualityCheck } = options;
|
||||
let lastResult;
|
||||
let resultsCount = 0;
|
||||
function memoized() {
|
||||
var _a, _b;
|
||||
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) {
|
||||
let objectCache = cacheNode.o;
|
||||
if (objectCache === null) {
|
||||
cacheNode.o = objectCache = /* @__PURE__ */ new WeakMap();
|
||||
}
|
||||
const objectNode = objectCache.get(arg);
|
||||
if (objectNode === void 0) {
|
||||
cacheNode = createCacheNode();
|
||||
objectCache.set(arg, cacheNode);
|
||||
} else {
|
||||
cacheNode = objectNode;
|
||||
}
|
||||
} else {
|
||||
let primitiveCache = cacheNode.p;
|
||||
if (primitiveCache === null) {
|
||||
cacheNode.p = primitiveCache = /* @__PURE__ */ new Map();
|
||||
}
|
||||
const primitiveNode = primitiveCache.get(arg);
|
||||
if (primitiveNode === void 0) {
|
||||
cacheNode = createCacheNode();
|
||||
primitiveCache.set(arg, cacheNode);
|
||||
} else {
|
||||
cacheNode = primitiveNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
const terminatedNode = cacheNode;
|
||||
let result;
|
||||
if (cacheNode.s === TERMINATED) {
|
||||
result = cacheNode.v;
|
||||
} else {
|
||||
result = func.apply(null, arguments);
|
||||
resultsCount++;
|
||||
if (resultEqualityCheck) {
|
||||
const lastResultValue = (_b = (_a = lastResult == null ? void 0 : lastResult.deref) == null ? void 0 : _a.call(lastResult)) != null ? _b : lastResult;
|
||||
if (lastResultValue != null && resultEqualityCheck(lastResultValue, 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;
|
||||
}
|
||||
|
||||
// src/createSelectorCreator.ts
|
||||
function createSelectorCreator(memoizeOrOptions, ...memoizeOptionsFromArgs) {
|
||||
const createSelectorCreatorOptions = typeof memoizeOrOptions === "function" ? {
|
||||
memoize: memoizeOrOptions,
|
||||
memoizeOptions: memoizeOptionsFromArgs
|
||||
} : memoizeOrOptions;
|
||||
const createSelector2 = (...createSelectorArgs) => {
|
||||
let recomputations = 0;
|
||||
let dependencyRecomputations = 0;
|
||||
let lastResult;
|
||||
let directlyPassedOptions = {};
|
||||
let resultFunc = createSelectorArgs.pop();
|
||||
if (typeof resultFunc === "object") {
|
||||
directlyPassedOptions = resultFunc;
|
||||
resultFunc = createSelectorArgs.pop();
|
||||
}
|
||||
assertIsFunction(
|
||||
resultFunc,
|
||||
`createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`
|
||||
);
|
||||
const combinedOptions = __spreadValues(__spreadValues({}, createSelectorCreatorOptions), directlyPassedOptions);
|
||||
const {
|
||||
memoize,
|
||||
memoizeOptions = [],
|
||||
argsMemoize = weakMapMemoize,
|
||||
argsMemoizeOptions = [],
|
||||
devModeChecks = {}
|
||||
} = combinedOptions;
|
||||
const finalMemoizeOptions = ensureIsArray(memoizeOptions);
|
||||
const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions);
|
||||
const dependencies = getDependencies(createSelectorArgs);
|
||||
const memoizedResultFunc = memoize(function recomputationWrapper() {
|
||||
recomputations++;
|
||||
return resultFunc.apply(
|
||||
null,
|
||||
arguments
|
||||
);
|
||||
}, ...finalMemoizeOptions);
|
||||
let firstRun = true;
|
||||
const selector = argsMemoize(function dependenciesChecker() {
|
||||
dependencyRecomputations++;
|
||||
const inputSelectorResults = collectInputSelectorResults(
|
||||
dependencies,
|
||||
arguments
|
||||
);
|
||||
lastResult = memoizedResultFunc.apply(null, inputSelectorResults);
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
const { identityFunctionCheck, inputStabilityCheck } = getDevModeChecksExecutionInfo(firstRun, devModeChecks);
|
||||
if (identityFunctionCheck.shouldRun) {
|
||||
identityFunctionCheck.run(
|
||||
resultFunc,
|
||||
inputSelectorResults,
|
||||
lastResult
|
||||
);
|
||||
}
|
||||
if (inputStabilityCheck.shouldRun) {
|
||||
const inputSelectorResultsCopy = collectInputSelectorResults(
|
||||
dependencies,
|
||||
arguments
|
||||
);
|
||||
inputStabilityCheck.run(
|
||||
{ inputSelectorResults, inputSelectorResultsCopy },
|
||||
{ memoize, memoizeOptions: finalMemoizeOptions },
|
||||
arguments
|
||||
);
|
||||
}
|
||||
if (firstRun)
|
||||
firstRun = false;
|
||||
}
|
||||
return lastResult;
|
||||
}, ...finalArgsMemoizeOptions);
|
||||
return Object.assign(selector, {
|
||||
resultFunc,
|
||||
memoizedResultFunc,
|
||||
dependencies,
|
||||
dependencyRecomputations: () => dependencyRecomputations,
|
||||
resetDependencyRecomputations: () => {
|
||||
dependencyRecomputations = 0;
|
||||
},
|
||||
lastResult: () => lastResult,
|
||||
recomputations: () => recomputations,
|
||||
resetRecomputations: () => {
|
||||
recomputations = 0;
|
||||
},
|
||||
memoize,
|
||||
argsMemoize
|
||||
});
|
||||
};
|
||||
Object.assign(createSelector2, {
|
||||
withTypes: () => createSelector2
|
||||
});
|
||||
return createSelector2;
|
||||
}
|
||||
var createSelector = /* @__PURE__ */ createSelectorCreator(weakMapMemoize);
|
||||
|
||||
// src/createStructuredSelector.ts
|
||||
var createStructuredSelector = Object.assign(
|
||||
(inputSelectorsObject, selectorCreator = createSelector) => {
|
||||
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) => {
|
||||
return inputSelectorResults.reduce((composition, value, index) => {
|
||||
composition[inputSelectorKeys[index]] = value;
|
||||
return composition;
|
||||
}, {});
|
||||
}
|
||||
);
|
||||
return structuredSelector;
|
||||
},
|
||||
{ withTypes: () => createStructuredSelector }
|
||||
);
|
||||
export {
|
||||
createSelector,
|
||||
createSelectorCreator,
|
||||
createStructuredSelector,
|
||||
lruMemoize,
|
||||
referenceEqualityCheck,
|
||||
setGlobalDevModeChecks,
|
||||
autotrackMemoize as unstable_autotrackMemoize,
|
||||
weakMapMemoize
|
||||
};
|
||||
//# sourceMappingURL=reselect.legacy-esm.js.map
|
||||
1
node_modules/reselect/dist/reselect.legacy-esm.js.map
generated
vendored
Normal file
1
node_modules/reselect/dist/reselect.legacy-esm.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
743
node_modules/reselect/dist/reselect.mjs
generated
vendored
Normal file
743
node_modules/reselect/dist/reselect.mjs
generated
vendored
Normal file
|
|
@ -0,0 +1,743 @@
|
|||
// src/devModeChecks/identityFunctionCheck.ts
|
||||
var runIdentityFunctionCheck = (resultFunc, inputSelectorsResults, outputSelectorResult) => {
|
||||
if (inputSelectorsResults.length === 1 && inputSelectorsResults[0] === outputSelectorResult) {
|
||||
let isInputSameAsOutput = false;
|
||||
try {
|
||||
const emptyObject = {};
|
||||
if (resultFunc(emptyObject) === emptyObject)
|
||||
isInputSameAsOutput = true;
|
||||
} catch {
|
||||
}
|
||||
if (isInputSameAsOutput) {
|
||||
let stack = void 0;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
;
|
||||
({ stack } = e);
|
||||
}
|
||||
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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// src/devModeChecks/inputStabilityCheck.ts
|
||||
var runInputStabilityCheck = (inputSelectorResultsObject, options, inputSelectorArgs) => {
|
||||
const { memoize, memoizeOptions } = options;
|
||||
const { inputSelectorResults, inputSelectorResultsCopy } = inputSelectorResultsObject;
|
||||
const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions);
|
||||
const areInputSelectorResultsEqual = createAnEmptyObject.apply(null, inputSelectorResults) === createAnEmptyObject.apply(null, inputSelectorResultsCopy);
|
||||
if (!areInputSelectorResultsEqual) {
|
||||
let stack = void 0;
|
||||
try {
|
||||
throw new Error();
|
||||
} catch (e) {
|
||||
;
|
||||
({ stack } = e);
|
||||
}
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// src/devModeChecks/setGlobalDevModeChecks.ts
|
||||
var globalDevModeChecks = {
|
||||
inputStabilityCheck: "once",
|
||||
identityFunctionCheck: "once"
|
||||
};
|
||||
var setGlobalDevModeChecks = (devModeChecks) => {
|
||||
Object.assign(globalDevModeChecks, devModeChecks);
|
||||
};
|
||||
|
||||
// src/utils.ts
|
||||
var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
|
||||
function assertIsFunction(func, errorMessage = `expected a function, instead received ${typeof func}`) {
|
||||
if (typeof func !== "function") {
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
}
|
||||
function assertIsObject(object, errorMessage = `expected an object, instead received ${typeof object}`) {
|
||||
if (typeof object !== "object") {
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
}
|
||||
function assertIsArrayOfFunctions(array, errorMessage = `expected all items to be functions, instead received the following types: `) {
|
||||
if (!array.every((item) => typeof item === "function")) {
|
||||
const itemTypes = array.map(
|
||||
(item) => typeof item === "function" ? `function ${item.name || "unnamed"}()` : typeof item
|
||||
).join(", ");
|
||||
throw new TypeError(`${errorMessage}[${itemTypes}]`);
|
||||
}
|
||||
}
|
||||
var ensureIsArray = (item) => {
|
||||
return Array.isArray(item) ? item : [item];
|
||||
};
|
||||
function getDependencies(createSelectorArgs) {
|
||||
const dependencies = Array.isArray(createSelectorArgs[0]) ? createSelectorArgs[0] : createSelectorArgs;
|
||||
assertIsArrayOfFunctions(
|
||||
dependencies,
|
||||
`createSelector expects all input-selectors to be functions, but received the following types: `
|
||||
);
|
||||
return dependencies;
|
||||
}
|
||||
function collectInputSelectorResults(dependencies, inputSelectorArgs) {
|
||||
const inputSelectorResults = [];
|
||||
const { length } = dependencies;
|
||||
for (let i = 0; i < length; i++) {
|
||||
inputSelectorResults.push(dependencies[i].apply(null, inputSelectorArgs));
|
||||
}
|
||||
return inputSelectorResults;
|
||||
}
|
||||
var getDevModeChecksExecutionInfo = (firstRun, 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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// src/autotrackMemoize/autotracking.ts
|
||||
var $REVISION = 0;
|
||||
var CURRENT_TRACKER = null;
|
||||
var Cell = class {
|
||||
revision = $REVISION;
|
||||
_value;
|
||||
_lastValue;
|
||||
_isEqual = tripleEq;
|
||||
constructor(initialValue, isEqual = 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, b) {
|
||||
return a === b;
|
||||
}
|
||||
var TrackingCache = class {
|
||||
_cachedValue;
|
||||
_cachedRevision = -1;
|
||||
_deps = [];
|
||||
hits = 0;
|
||||
fn;
|
||||
constructor(fn) {
|
||||
this.fn = fn;
|
||||
}
|
||||
clear() {
|
||||
this._cachedValue = void 0;
|
||||
this._cachedRevision = -1;
|
||||
this._deps = [];
|
||||
this.hits = 0;
|
||||
}
|
||||
get value() {
|
||||
if (this.revision > this._cachedRevision) {
|
||||
const { fn } = this;
|
||||
const currentTracker = /* @__PURE__ */ new Set();
|
||||
const prevTracker = CURRENT_TRACKER;
|
||||
CURRENT_TRACKER = currentTracker;
|
||||
this._cachedValue = fn();
|
||||
CURRENT_TRACKER = prevTracker;
|
||||
this.hits++;
|
||||
this._deps = Array.from(currentTracker);
|
||||
this._cachedRevision = this.revision;
|
||||
}
|
||||
CURRENT_TRACKER?.add(this);
|
||||
return this._cachedValue;
|
||||
}
|
||||
get revision() {
|
||||
return Math.max(...this._deps.map((d) => d.revision), 0);
|
||||
}
|
||||
};
|
||||
function getValue(cell) {
|
||||
if (!(cell instanceof Cell)) {
|
||||
console.warn("Not a valid cell! ", cell);
|
||||
}
|
||||
return cell.value;
|
||||
}
|
||||
function setValue(storage, value) {
|
||||
if (!(storage instanceof Cell)) {
|
||||
throw new TypeError(
|
||||
"setValue must be passed a tracked store created with `createStorage`."
|
||||
);
|
||||
}
|
||||
storage.value = storage._lastValue = value;
|
||||
}
|
||||
function createCell(initialValue, isEqual = tripleEq) {
|
||||
return new Cell(initialValue, isEqual);
|
||||
}
|
||||
function createCache(fn) {
|
||||
assertIsFunction(
|
||||
fn,
|
||||
"the first parameter to `createCache` must be a function"
|
||||
);
|
||||
return new TrackingCache(fn);
|
||||
}
|
||||
|
||||
// src/autotrackMemoize/tracking.ts
|
||||
var neverEq = (a, b) => false;
|
||||
function createTag() {
|
||||
return createCell(null, neverEq);
|
||||
}
|
||||
function dirtyTag(tag, value) {
|
||||
setValue(tag, value);
|
||||
}
|
||||
var consumeCollection = (node) => {
|
||||
let tag = node.collectionTag;
|
||||
if (tag === null) {
|
||||
tag = node.collectionTag = createTag();
|
||||
}
|
||||
getValue(tag);
|
||||
};
|
||||
var dirtyCollection = (node) => {
|
||||
const tag = node.collectionTag;
|
||||
if (tag !== null) {
|
||||
dirtyTag(tag, null);
|
||||
}
|
||||
};
|
||||
|
||||
// src/autotrackMemoize/proxy.ts
|
||||
var REDUX_PROXY_LABEL = Symbol();
|
||||
var nextId = 0;
|
||||
var proto = Object.getPrototypeOf({});
|
||||
var ObjectTreeNode = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.value = value;
|
||||
this.tag.value = value;
|
||||
}
|
||||
proxy = new Proxy(this, objectProxyHandler);
|
||||
tag = createTag();
|
||||
tags = {};
|
||||
children = {};
|
||||
collectionTag = null;
|
||||
id = nextId++;
|
||||
};
|
||||
var objectProxyHandler = {
|
||||
get(node, key) {
|
||||
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 === void 0) {
|
||||
childNode = node.children[key] = createNode(childValue);
|
||||
}
|
||||
if (childNode.tag) {
|
||||
getValue(childNode.tag);
|
||||
}
|
||||
return childNode.proxy;
|
||||
} else {
|
||||
let tag = node.tags[key];
|
||||
if (tag === void 0) {
|
||||
tag = node.tags[key] = createTag();
|
||||
tag.value = childValue;
|
||||
}
|
||||
getValue(tag);
|
||||
return childValue;
|
||||
}
|
||||
}
|
||||
const res = calculateResult();
|
||||
return res;
|
||||
},
|
||||
ownKeys(node) {
|
||||
consumeCollection(node);
|
||||
return Reflect.ownKeys(node.value);
|
||||
},
|
||||
getOwnPropertyDescriptor(node, prop) {
|
||||
return Reflect.getOwnPropertyDescriptor(node.value, prop);
|
||||
},
|
||||
has(node, prop) {
|
||||
return Reflect.has(node.value, prop);
|
||||
}
|
||||
};
|
||||
var ArrayTreeNode = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
this.value = value;
|
||||
this.tag.value = value;
|
||||
}
|
||||
proxy = new Proxy([this], arrayProxyHandler);
|
||||
tag = createTag();
|
||||
tags = {};
|
||||
children = {};
|
||||
collectionTag = null;
|
||||
id = nextId++;
|
||||
};
|
||||
var arrayProxyHandler = {
|
||||
get([node], key) {
|
||||
if (key === "length") {
|
||||
consumeCollection(node);
|
||||
}
|
||||
return objectProxyHandler.get(node, key);
|
||||
},
|
||||
ownKeys([node]) {
|
||||
return objectProxyHandler.ownKeys(node);
|
||||
},
|
||||
getOwnPropertyDescriptor([node], prop) {
|
||||
return objectProxyHandler.getOwnPropertyDescriptor(node, prop);
|
||||
},
|
||||
has([node], prop) {
|
||||
return objectProxyHandler.has(node, prop);
|
||||
}
|
||||
};
|
||||
function createNode(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return new ArrayTreeNode(value);
|
||||
}
|
||||
return new ObjectTreeNode(value);
|
||||
}
|
||||
function updateNode(node, newValue) {
|
||||
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[key];
|
||||
const newChildValue = newValue[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[key];
|
||||
const childValue = childNode.value;
|
||||
if (childValue === newChildValue) {
|
||||
continue;
|
||||
} else if (typeof newChildValue === "object" && newChildValue !== null) {
|
||||
updateNode(childNode, newChildValue);
|
||||
} else {
|
||||
deleteNode(childNode);
|
||||
delete children[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
function deleteNode(node) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// src/lruMemoize.ts
|
||||
function createSingletonCache(equals) {
|
||||
let entry;
|
||||
return {
|
||||
get(key) {
|
||||
if (entry && equals(entry.key, key)) {
|
||||
return entry.value;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
},
|
||||
put(key, value) {
|
||||
entry = { key, value };
|
||||
},
|
||||
getEntries() {
|
||||
return entry ? [entry] : [];
|
||||
},
|
||||
clear() {
|
||||
entry = void 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
function createLruCache(maxSize, equals) {
|
||||
let entries = [];
|
||||
function get(key) {
|
||||
const cacheIndex = entries.findIndex((entry) => equals(key, entry.key));
|
||||
if (cacheIndex > -1) {
|
||||
const entry = entries[cacheIndex];
|
||||
if (cacheIndex > 0) {
|
||||
entries.splice(cacheIndex, 1);
|
||||
entries.unshift(entry);
|
||||
}
|
||||
return entry.value;
|
||||
}
|
||||
return NOT_FOUND;
|
||||
}
|
||||
function put(key, value) {
|
||||
if (get(key) === NOT_FOUND) {
|
||||
entries.unshift({ key, value });
|
||||
if (entries.length > maxSize) {
|
||||
entries.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
function getEntries() {
|
||||
return entries;
|
||||
}
|
||||
function clear() {
|
||||
entries = [];
|
||||
}
|
||||
return { get, put, getEntries, clear };
|
||||
}
|
||||
var referenceEqualityCheck = (a, b) => a === b;
|
||||
function createCacheKeyComparator(equalityCheck) {
|
||||
return function areArgumentsShallowlyEqual(prev, next) {
|
||||
if (prev === null || next === null || prev.length !== next.length) {
|
||||
return false;
|
||||
}
|
||||
const { length } = prev;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (!equalityCheck(prev[i], next[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
function lruMemoize(func, equalityCheckOrOptions) {
|
||||
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);
|
||||
if (value === NOT_FOUND) {
|
||||
value = func.apply(null, arguments);
|
||||
resultsCount++;
|
||||
if (resultEqualityCheck) {
|
||||
const entries = cache.getEntries();
|
||||
const matchingEntry = entries.find(
|
||||
(entry) => resultEqualityCheck(entry.value, value)
|
||||
);
|
||||
if (matchingEntry) {
|
||||
value = matchingEntry.value;
|
||||
resultsCount !== 0 && resultsCount--;
|
||||
}
|
||||
}
|
||||
cache.put(arguments, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
memoized.clearCache = () => {
|
||||
cache.clear();
|
||||
memoized.resetResultsCount();
|
||||
};
|
||||
memoized.resultsCount = () => resultsCount;
|
||||
memoized.resetResultsCount = () => {
|
||||
resultsCount = 0;
|
||||
};
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// src/autotrackMemoize/autotrackMemoize.ts
|
||||
function autotrackMemoize(func) {
|
||||
const node = createNode(
|
||||
[]
|
||||
);
|
||||
let lastArgs = null;
|
||||
const shallowEqual = createCacheKeyComparator(referenceEqualityCheck);
|
||||
const cache = createCache(() => {
|
||||
const res = func.apply(null, node.proxy);
|
||||
return res;
|
||||
});
|
||||
function memoized() {
|
||||
if (!shallowEqual(lastArgs, arguments)) {
|
||||
updateNode(node, arguments);
|
||||
lastArgs = arguments;
|
||||
}
|
||||
return cache.value;
|
||||
}
|
||||
memoized.clearCache = () => {
|
||||
return cache.clear();
|
||||
};
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// src/weakMapMemoize.ts
|
||||
var StrongRef = class {
|
||||
constructor(value) {
|
||||
this.value = value;
|
||||
}
|
||||
deref() {
|
||||
return this.value;
|
||||
}
|
||||
};
|
||||
var Ref = typeof WeakRef !== "undefined" ? WeakRef : StrongRef;
|
||||
var UNTERMINATED = 0;
|
||||
var TERMINATED = 1;
|
||||
function createCacheNode() {
|
||||
return {
|
||||
s: UNTERMINATED,
|
||||
v: void 0,
|
||||
o: null,
|
||||
p: null
|
||||
};
|
||||
}
|
||||
function weakMapMemoize(func, options = {}) {
|
||||
let fnNode = createCacheNode();
|
||||
const { resultEqualityCheck } = options;
|
||||
let lastResult;
|
||||
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) {
|
||||
let objectCache = cacheNode.o;
|
||||
if (objectCache === null) {
|
||||
cacheNode.o = objectCache = /* @__PURE__ */ new WeakMap();
|
||||
}
|
||||
const objectNode = objectCache.get(arg);
|
||||
if (objectNode === void 0) {
|
||||
cacheNode = createCacheNode();
|
||||
objectCache.set(arg, cacheNode);
|
||||
} else {
|
||||
cacheNode = objectNode;
|
||||
}
|
||||
} else {
|
||||
let primitiveCache = cacheNode.p;
|
||||
if (primitiveCache === null) {
|
||||
cacheNode.p = primitiveCache = /* @__PURE__ */ new Map();
|
||||
}
|
||||
const primitiveNode = primitiveCache.get(arg);
|
||||
if (primitiveNode === void 0) {
|
||||
cacheNode = createCacheNode();
|
||||
primitiveCache.set(arg, cacheNode);
|
||||
} else {
|
||||
cacheNode = primitiveNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
const terminatedNode = cacheNode;
|
||||
let result;
|
||||
if (cacheNode.s === TERMINATED) {
|
||||
result = cacheNode.v;
|
||||
} else {
|
||||
result = func.apply(null, arguments);
|
||||
resultsCount++;
|
||||
if (resultEqualityCheck) {
|
||||
const lastResultValue = lastResult?.deref?.() ?? lastResult;
|
||||
if (lastResultValue != null && resultEqualityCheck(lastResultValue, 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;
|
||||
}
|
||||
|
||||
// src/createSelectorCreator.ts
|
||||
function createSelectorCreator(memoizeOrOptions, ...memoizeOptionsFromArgs) {
|
||||
const createSelectorCreatorOptions = typeof memoizeOrOptions === "function" ? {
|
||||
memoize: memoizeOrOptions,
|
||||
memoizeOptions: memoizeOptionsFromArgs
|
||||
} : memoizeOrOptions;
|
||||
const createSelector2 = (...createSelectorArgs) => {
|
||||
let recomputations = 0;
|
||||
let dependencyRecomputations = 0;
|
||||
let lastResult;
|
||||
let directlyPassedOptions = {};
|
||||
let resultFunc = createSelectorArgs.pop();
|
||||
if (typeof resultFunc === "object") {
|
||||
directlyPassedOptions = resultFunc;
|
||||
resultFunc = createSelectorArgs.pop();
|
||||
}
|
||||
assertIsFunction(
|
||||
resultFunc,
|
||||
`createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`
|
||||
);
|
||||
const combinedOptions = {
|
||||
...createSelectorCreatorOptions,
|
||||
...directlyPassedOptions
|
||||
};
|
||||
const {
|
||||
memoize,
|
||||
memoizeOptions = [],
|
||||
argsMemoize = weakMapMemoize,
|
||||
argsMemoizeOptions = [],
|
||||
devModeChecks = {}
|
||||
} = combinedOptions;
|
||||
const finalMemoizeOptions = ensureIsArray(memoizeOptions);
|
||||
const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions);
|
||||
const dependencies = getDependencies(createSelectorArgs);
|
||||
const memoizedResultFunc = memoize(function recomputationWrapper() {
|
||||
recomputations++;
|
||||
return resultFunc.apply(
|
||||
null,
|
||||
arguments
|
||||
);
|
||||
}, ...finalMemoizeOptions);
|
||||
let firstRun = true;
|
||||
const selector = argsMemoize(function dependenciesChecker() {
|
||||
dependencyRecomputations++;
|
||||
const inputSelectorResults = collectInputSelectorResults(
|
||||
dependencies,
|
||||
arguments
|
||||
);
|
||||
lastResult = memoizedResultFunc.apply(null, inputSelectorResults);
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
const { identityFunctionCheck, inputStabilityCheck } = getDevModeChecksExecutionInfo(firstRun, devModeChecks);
|
||||
if (identityFunctionCheck.shouldRun) {
|
||||
identityFunctionCheck.run(
|
||||
resultFunc,
|
||||
inputSelectorResults,
|
||||
lastResult
|
||||
);
|
||||
}
|
||||
if (inputStabilityCheck.shouldRun) {
|
||||
const inputSelectorResultsCopy = collectInputSelectorResults(
|
||||
dependencies,
|
||||
arguments
|
||||
);
|
||||
inputStabilityCheck.run(
|
||||
{ inputSelectorResults, inputSelectorResultsCopy },
|
||||
{ memoize, memoizeOptions: finalMemoizeOptions },
|
||||
arguments
|
||||
);
|
||||
}
|
||||
if (firstRun)
|
||||
firstRun = false;
|
||||
}
|
||||
return lastResult;
|
||||
}, ...finalArgsMemoizeOptions);
|
||||
return Object.assign(selector, {
|
||||
resultFunc,
|
||||
memoizedResultFunc,
|
||||
dependencies,
|
||||
dependencyRecomputations: () => dependencyRecomputations,
|
||||
resetDependencyRecomputations: () => {
|
||||
dependencyRecomputations = 0;
|
||||
},
|
||||
lastResult: () => lastResult,
|
||||
recomputations: () => recomputations,
|
||||
resetRecomputations: () => {
|
||||
recomputations = 0;
|
||||
},
|
||||
memoize,
|
||||
argsMemoize
|
||||
});
|
||||
};
|
||||
Object.assign(createSelector2, {
|
||||
withTypes: () => createSelector2
|
||||
});
|
||||
return createSelector2;
|
||||
}
|
||||
var createSelector = /* @__PURE__ */ createSelectorCreator(weakMapMemoize);
|
||||
|
||||
// src/createStructuredSelector.ts
|
||||
var createStructuredSelector = Object.assign(
|
||||
(inputSelectorsObject, selectorCreator = createSelector) => {
|
||||
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) => {
|
||||
return inputSelectorResults.reduce((composition, value, index) => {
|
||||
composition[inputSelectorKeys[index]] = value;
|
||||
return composition;
|
||||
}, {});
|
||||
}
|
||||
);
|
||||
return structuredSelector;
|
||||
},
|
||||
{ withTypes: () => createStructuredSelector }
|
||||
);
|
||||
export {
|
||||
createSelector,
|
||||
createSelectorCreator,
|
||||
createStructuredSelector,
|
||||
lruMemoize,
|
||||
referenceEqualityCheck,
|
||||
setGlobalDevModeChecks,
|
||||
autotrackMemoize as unstable_autotrackMemoize,
|
||||
weakMapMemoize
|
||||
};
|
||||
//# sourceMappingURL=reselect.mjs.map
|
||||
1
node_modules/reselect/dist/reselect.mjs.map
generated
vendored
Normal file
1
node_modules/reselect/dist/reselect.mjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
88
node_modules/reselect/package.json
generated
vendored
Normal file
88
node_modules/reselect/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"name": "reselect",
|
||||
"version": "5.1.1",
|
||||
"description": "Selectors for Redux.",
|
||||
"main": "./dist/cjs/reselect.cjs",
|
||||
"module": "./dist/reselect.legacy-esm.js",
|
||||
"types": "./dist/reselect.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"types": "./dist/reselect.d.ts",
|
||||
"import": "./dist/reselect.mjs",
|
||||
"default": "./dist/cjs/reselect.cjs"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"bugs": {
|
||||
"url": "https://github.com/reduxjs/reselect/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rimraf dist",
|
||||
"format": "prettier --write \"{src,test}/**/*.{js,ts}\" \"docs/**/*.md\"",
|
||||
"lint": "eslint src test",
|
||||
"prepack": "yarn build",
|
||||
"bench": "vitest --run bench --mode production",
|
||||
"test": "node --expose-gc ./node_modules/vitest/dist/cli-wrapper.js --run && vitest --run --typecheck.only",
|
||||
"test:watch": "node --expose-gc ./node_modules/vitest/dist/cli-wrapper.js --watch",
|
||||
"test:cov": "vitest run --coverage",
|
||||
"type-check": "vitest --run --typecheck.only",
|
||||
"type-check:trace": "vitest --run --typecheck.only && tsc --noEmit -p typescript_test/tsconfig.json --generateTrace trace && npx @typescript/analyze-trace trace && rimraf trace",
|
||||
"test:typescript": "tsc --noEmit -p typescript_test/tsconfig.json",
|
||||
"docs:start": "yarn --cwd website start",
|
||||
"docs:build": "yarn --cwd website build",
|
||||
"docs:clear": "yarn --cwd website clear",
|
||||
"docs:serve": "yarn --cwd website serve"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"redux"
|
||||
],
|
||||
"authors": [
|
||||
"Lee Bannard",
|
||||
"Robert Binna",
|
||||
"Martijn Faassen",
|
||||
"Philip Spitzlinger"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/reselect.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@types/lodash": "^4.14.175",
|
||||
"@types/react": "^18.2.38",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/shelljs": "^0.8.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^6",
|
||||
"@typescript-eslint/parser": "^6",
|
||||
"@typescript/analyze-trace": "^0.10.1",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-typescript": "0.14.0",
|
||||
"jsdom": "^23.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.memoize": "^4.1.2",
|
||||
"memoize-one": "^6.0.0",
|
||||
"micro-memoize": "^4.0.9",
|
||||
"netlify-plugin-cache": "^1.0.3",
|
||||
"prettier": "^2.7.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^9.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"shelljs": "^0.8.5",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.4.2",
|
||||
"vitest": "^1.1.1"
|
||||
},
|
||||
"packageManager": "yarn@4.1.0"
|
||||
}
|
||||
100
node_modules/reselect/src/autotrackMemoize/autotrackMemoize.ts
generated
vendored
Normal file
100
node_modules/reselect/src/autotrackMemoize/autotrackMemoize.ts
generated
vendored
Normal 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>
|
||||
}
|
||||
159
node_modules/reselect/src/autotrackMemoize/autotracking.ts
generated
vendored
Normal file
159
node_modules/reselect/src/autotrackMemoize/autotracking.ts
generated
vendored
Normal 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
230
node_modules/reselect/src/autotrackMemoize/proxy.ts
generated
vendored
Normal 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
50
node_modules/reselect/src/autotrackMemoize/tracking.ts
generated
vendored
Normal 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
9
node_modules/reselect/src/autotrackMemoize/utils.ts
generated
vendored
Normal 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
493
node_modules/reselect/src/createSelectorCreator.ts
generated
vendored
Normal 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
454
node_modules/reselect/src/createStructuredSelector.ts
generated
vendored
Normal 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
|
||||
53
node_modules/reselect/src/devModeChecks/identityFunctionCheck.ts
generated
vendored
Normal file
53
node_modules/reselect/src/devModeChecks/identityFunctionCheck.ts
generated
vendored
Normal 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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
59
node_modules/reselect/src/devModeChecks/inputStabilityCheck.ts
generated
vendored
Normal file
59
node_modules/reselect/src/devModeChecks/inputStabilityCheck.ts
generated
vendored
Normal 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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
63
node_modules/reselect/src/devModeChecks/setGlobalDevModeChecks.ts
generated
vendored
Normal file
63
node_modules/reselect/src/devModeChecks/setGlobalDevModeChecks.ts
generated
vendored
Normal 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
36
node_modules/reselect/src/index.ts
generated
vendored
Normal 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
249
node_modules/reselect/src/lruMemoize.ts
generated
vendored
Normal 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
876
node_modules/reselect/src/types.ts
generated
vendored
Normal 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
156
node_modules/reselect/src/utils.ts
generated
vendored
Normal 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
1
node_modules/reselect/src/versionedTypes/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export type { MergeParameters } from './ts47-mergeParameters'
|
||||
117
node_modules/reselect/src/versionedTypes/ts47-mergeParameters.ts
generated
vendored
Normal file
117
node_modules/reselect/src/versionedTypes/ts47-mergeParameters.ts
generated
vendored
Normal 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
269
node_modules/reselect/src/weakMapMemoize.ts
generated
vendored
Normal 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>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue