[d24f17c] | 1 | import { runIdentityFunctionCheck } from './devModeChecks/identityFunctionCheck'
|
---|
| 2 | import { runInputStabilityCheck } from './devModeChecks/inputStabilityCheck'
|
---|
| 3 | import { globalDevModeChecks } from './devModeChecks/setGlobalDevModeChecks'
|
---|
| 4 | // eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
---|
| 5 | import type {
|
---|
| 6 | DevModeChecks,
|
---|
| 7 | Selector,
|
---|
| 8 | SelectorArray,
|
---|
| 9 | DevModeChecksExecutionInfo
|
---|
| 10 | } from './types'
|
---|
| 11 |
|
---|
| 12 | export const NOT_FOUND = 'NOT_FOUND'
|
---|
| 13 | export type NOT_FOUND_TYPE = typeof NOT_FOUND
|
---|
| 14 |
|
---|
| 15 | /**
|
---|
| 16 | * Assert that the provided value is a function. If the assertion fails,
|
---|
| 17 | * a `TypeError` is thrown with an optional custom error message.
|
---|
| 18 | *
|
---|
| 19 | * @param func - The value to be checked.
|
---|
| 20 | * @param errorMessage - An optional custom error message to use if the assertion fails.
|
---|
| 21 | * @throws A `TypeError` if the assertion fails.
|
---|
| 22 | */
|
---|
| 23 | export function assertIsFunction<FunctionType extends Function>(
|
---|
| 24 | func: unknown,
|
---|
| 25 | errorMessage = `expected a function, instead received ${typeof func}`
|
---|
| 26 | ): asserts func is FunctionType {
|
---|
| 27 | if (typeof func !== 'function') {
|
---|
| 28 | throw new TypeError(errorMessage)
|
---|
| 29 | }
|
---|
| 30 | }
|
---|
| 31 |
|
---|
| 32 | /**
|
---|
| 33 | * Assert that the provided value is an object. If the assertion fails,
|
---|
| 34 | * a `TypeError` is thrown with an optional custom error message.
|
---|
| 35 | *
|
---|
| 36 | * @param object - The value to be checked.
|
---|
| 37 | * @param errorMessage - An optional custom error message to use if the assertion fails.
|
---|
| 38 | * @throws A `TypeError` if the assertion fails.
|
---|
| 39 | */
|
---|
| 40 | export function assertIsObject<ObjectType extends Record<string, unknown>>(
|
---|
| 41 | object: unknown,
|
---|
| 42 | errorMessage = `expected an object, instead received ${typeof object}`
|
---|
| 43 | ): asserts object is ObjectType {
|
---|
| 44 | if (typeof object !== 'object') {
|
---|
| 45 | throw new TypeError(errorMessage)
|
---|
| 46 | }
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | /**
|
---|
| 50 | * Assert that the provided array is an array of functions. If the assertion fails,
|
---|
| 51 | * a `TypeError` is thrown with an optional custom error message.
|
---|
| 52 | *
|
---|
| 53 | * @param array - The array to be checked.
|
---|
| 54 | * @param errorMessage - An optional custom error message to use if the assertion fails.
|
---|
| 55 | * @throws A `TypeError` if the assertion fails.
|
---|
| 56 | */
|
---|
| 57 | export function assertIsArrayOfFunctions<FunctionType extends Function>(
|
---|
| 58 | array: unknown[],
|
---|
| 59 | errorMessage = `expected all items to be functions, instead received the following types: `
|
---|
| 60 | ): asserts array is FunctionType[] {
|
---|
| 61 | if (
|
---|
| 62 | !array.every((item): item is FunctionType => typeof item === 'function')
|
---|
| 63 | ) {
|
---|
| 64 | const itemTypes = array
|
---|
| 65 | .map(item =>
|
---|
| 66 | typeof item === 'function'
|
---|
| 67 | ? `function ${item.name || 'unnamed'}()`
|
---|
| 68 | : typeof item
|
---|
| 69 | )
|
---|
| 70 | .join(', ')
|
---|
| 71 | throw new TypeError(`${errorMessage}[${itemTypes}]`)
|
---|
| 72 | }
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | /**
|
---|
| 76 | * Ensure that the input is an array. If it's already an array, it's returned as is.
|
---|
| 77 | * If it's not an array, it will be wrapped in a new array.
|
---|
| 78 | *
|
---|
| 79 | * @param item - The item to be checked.
|
---|
| 80 | * @returns An array containing the input item. If the input is already an array, it's returned without modification.
|
---|
| 81 | */
|
---|
| 82 | export const ensureIsArray = (item: unknown) => {
|
---|
| 83 | return Array.isArray(item) ? item : [item]
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | /**
|
---|
| 87 | * Extracts the "dependencies" / "input selectors" from the arguments of `createSelector`.
|
---|
| 88 | *
|
---|
| 89 | * @param createSelectorArgs - Arguments passed to `createSelector` as an array.
|
---|
| 90 | * @returns An array of "input selectors" / "dependencies".
|
---|
| 91 | * @throws A `TypeError` if any of the input selectors is not function.
|
---|
| 92 | */
|
---|
| 93 | export function getDependencies(createSelectorArgs: unknown[]) {
|
---|
| 94 | const dependencies = Array.isArray(createSelectorArgs[0])
|
---|
| 95 | ? createSelectorArgs[0]
|
---|
| 96 | : createSelectorArgs
|
---|
| 97 |
|
---|
| 98 | assertIsArrayOfFunctions<Selector>(
|
---|
| 99 | dependencies,
|
---|
| 100 | `createSelector expects all input-selectors to be functions, but received the following types: `
|
---|
| 101 | )
|
---|
| 102 |
|
---|
| 103 | return dependencies as SelectorArray
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | /**
|
---|
| 107 | * Runs each input selector and returns their collective results as an array.
|
---|
| 108 | *
|
---|
| 109 | * @param dependencies - An array of "dependencies" or "input selectors".
|
---|
| 110 | * @param inputSelectorArgs - An array of arguments being passed to the input selectors.
|
---|
| 111 | * @returns An array of input selector results.
|
---|
| 112 | */
|
---|
| 113 | export function collectInputSelectorResults(
|
---|
| 114 | dependencies: SelectorArray,
|
---|
| 115 | inputSelectorArgs: unknown[] | IArguments
|
---|
| 116 | ) {
|
---|
| 117 | const inputSelectorResults = []
|
---|
| 118 | const { length } = dependencies
|
---|
| 119 | for (let i = 0; i < length; i++) {
|
---|
| 120 | // @ts-ignore
|
---|
| 121 | // apply arguments instead of spreading and mutate a local list of params for performance.
|
---|
| 122 | inputSelectorResults.push(dependencies[i].apply(null, inputSelectorArgs))
|
---|
| 123 | }
|
---|
| 124 | return inputSelectorResults
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | /**
|
---|
| 128 | * Retrieves execution information for development mode checks.
|
---|
| 129 | *
|
---|
| 130 | * @param devModeChecks - Custom Settings for development mode checks. These settings will override the global defaults.
|
---|
| 131 | * @param firstRun - Indicates whether it is the first time the selector has run.
|
---|
| 132 | * @returns An object containing the execution information for each development mode check.
|
---|
| 133 | */
|
---|
| 134 | export const getDevModeChecksExecutionInfo = (
|
---|
| 135 | firstRun: boolean,
|
---|
| 136 | devModeChecks: Partial<DevModeChecks>
|
---|
| 137 | ) => {
|
---|
| 138 | const { identityFunctionCheck, inputStabilityCheck } = {
|
---|
| 139 | ...globalDevModeChecks,
|
---|
| 140 | ...devModeChecks
|
---|
| 141 | }
|
---|
| 142 | return {
|
---|
| 143 | identityFunctionCheck: {
|
---|
| 144 | shouldRun:
|
---|
| 145 | identityFunctionCheck === 'always' ||
|
---|
| 146 | (identityFunctionCheck === 'once' && firstRun),
|
---|
| 147 | run: runIdentityFunctionCheck
|
---|
| 148 | },
|
---|
| 149 | inputStabilityCheck: {
|
---|
| 150 | shouldRun:
|
---|
| 151 | inputStabilityCheck === 'always' ||
|
---|
| 152 | (inputStabilityCheck === 'once' && firstRun),
|
---|
| 153 | run: runInputStabilityCheck
|
---|
| 154 | }
|
---|
| 155 | } satisfies DevModeChecksExecutionInfo
|
---|
| 156 | }
|
---|