-
Notifications
You must be signed in to change notification settings - Fork 673
/
Copy pathutils.ts
156 lines (146 loc) · 5.4 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
}