eslint-plugin-react-x 5.24.1 → 5.24.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +15 -1766
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -145,7 +145,7 @@ const rules$6 = {
|
|
|
145
145
|
//#endregion
|
|
146
146
|
//#region package.json
|
|
147
147
|
var name$6 = "eslint-plugin-react-x";
|
|
148
|
-
var version = "5.24.
|
|
148
|
+
var version = "5.24.3";
|
|
149
149
|
|
|
150
150
|
//#endregion
|
|
151
151
|
//#region src/utils/create-rule.ts
|
|
@@ -257,17 +257,11 @@ const rule$1 = {
|
|
|
257
257
|
}
|
|
258
258
|
context.report(problem);
|
|
259
259
|
}
|
|
260
|
-
/**
|
|
261
|
-
* SourceCode that also works down to ESLint 3.0.0
|
|
262
|
-
*/
|
|
263
260
|
const getSourceCode = typeof context.getSourceCode === "function" ? () => {
|
|
264
261
|
return context.getSourceCode();
|
|
265
262
|
} : () => {
|
|
266
263
|
return context.sourceCode;
|
|
267
264
|
};
|
|
268
|
-
/**
|
|
269
|
-
* SourceCode#getScope that also works down to ESLint 3.0.0
|
|
270
|
-
*/
|
|
271
265
|
const getScope = typeof context.getScope === "function" ? () => {
|
|
272
266
|
return context.getScope();
|
|
273
267
|
} : (node) => {
|
|
@@ -287,9 +281,6 @@ const rule$1 = {
|
|
|
287
281
|
return result;
|
|
288
282
|
};
|
|
289
283
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Visitor for both function expressions and arrow function expressions.
|
|
292
|
-
*/
|
|
293
284
|
function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect, isAutoDepsHook) {
|
|
294
285
|
if (isEffect && node.async) reportProblem({
|
|
295
286
|
node,
|
|
@@ -989,24 +980,11 @@ function scanForConstructions({ declaredDependencies, declaredDependenciesNode,
|
|
|
989
980
|
isUsedOutsideOfHook: isUsedOutsideOfHook(ref)
|
|
990
981
|
}));
|
|
991
982
|
}
|
|
992
|
-
/**
|
|
993
|
-
* Assuming () means the passed/returned node:
|
|
994
|
-
* (props) => (props)
|
|
995
|
-
* props.(foo) => (props.foo)
|
|
996
|
-
* props.foo.(bar) => (props).foo.bar
|
|
997
|
-
* props.foo.bar.(baz) => (props).foo.bar.baz
|
|
998
|
-
*/
|
|
999
983
|
function getDependency(node) {
|
|
1000
984
|
if (node.parent && (node.parent.type === "MemberExpression" || node.parent.type === "OptionalMemberExpression") && node.parent.object === node && "name" in node.parent.property && node.parent.property.name !== "current" && !node.parent.computed && !(node.parent.parent != null && (node.parent.parent.type === "CallExpression" || node.parent.parent.type === "OptionalCallExpression") && node.parent.parent.callee === node.parent)) return getDependency(node.parent);
|
|
1001
985
|
else if (node.type === "MemberExpression" && node.parent && node.parent.type === "AssignmentExpression" && node.parent.left === node) return node.object;
|
|
1002
986
|
else return node;
|
|
1003
987
|
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Mark a node as either optional or required.
|
|
1006
|
-
* Note: If the node argument is an OptionalMemberExpression, it doesn't necessarily mean it is optional.
|
|
1007
|
-
* It just means there is an optional member somewhere inside.
|
|
1008
|
-
* This particular node might still represent a required member, so check .optional field.
|
|
1009
|
-
*/
|
|
1010
988
|
function markNode(node, optionalChains, result) {
|
|
1011
989
|
if (optionalChains) {
|
|
1012
990
|
if ("optional" in node && node.optional) {
|
|
@@ -1014,13 +992,6 @@ function markNode(node, optionalChains, result) {
|
|
|
1014
992
|
} else optionalChains.set(result, false);
|
|
1015
993
|
}
|
|
1016
994
|
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Assuming () means the passed node.
|
|
1019
|
-
* (foo) -> 'foo'
|
|
1020
|
-
* foo(.)bar -> 'foo.bar'
|
|
1021
|
-
* foo.bar(.)baz -> 'foo.bar.baz'
|
|
1022
|
-
* Otherwise throw.
|
|
1023
|
-
*/
|
|
1024
995
|
function analyzePropertyChain(node, optionalChains) {
|
|
1025
996
|
if (node.type === "Identifier" || node.type === "JSXIdentifier") {
|
|
1026
997
|
const result = node.name;
|
|
@@ -1067,16 +1038,6 @@ function getReactiveHookCallbackIndex(calleeNode, options) {
|
|
|
1067
1038
|
} else return -1;
|
|
1068
1039
|
}
|
|
1069
1040
|
}
|
|
1070
|
-
/**
|
|
1071
|
-
* ESLint won't assign node.parent to references from context.getScope()
|
|
1072
|
-
*
|
|
1073
|
-
* So instead we search for the node from an ancestor assigning node.parent
|
|
1074
|
-
* as we go. This mutates the AST.
|
|
1075
|
-
*
|
|
1076
|
-
* This traversal is:
|
|
1077
|
-
* - optimized by only searching nodes with a range surrounding our target node
|
|
1078
|
-
* - agnostic to AST node types, it looks for `{ type: string, ... }`
|
|
1079
|
-
*/
|
|
1080
1041
|
function fastFindReferenceWithParent(start, target) {
|
|
1081
1042
|
const queue = [start];
|
|
1082
1043
|
let item;
|
|
@@ -1127,10 +1088,6 @@ function getUnknownDependenciesMessage(reactiveHookName) {
|
|
|
1127
1088
|
|
|
1128
1089
|
//#endregion
|
|
1129
1090
|
//#region src/rules/globals/helpers.ts
|
|
1130
|
-
/**
|
|
1131
|
-
* Array methods that mutate the array in place.
|
|
1132
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
1133
|
-
*/
|
|
1134
1091
|
const MUTATING_ARRAY_METHODS = /* @__PURE__ */ new Set([
|
|
1135
1092
|
"copyWithin",
|
|
1136
1093
|
"fill",
|
|
@@ -1142,7 +1099,6 @@ const MUTATING_ARRAY_METHODS = /* @__PURE__ */ new Set([
|
|
|
1142
1099
|
"splice",
|
|
1143
1100
|
"unshift"
|
|
1144
1101
|
]);
|
|
1145
|
-
/** Resolve a direct call target, following simple function aliases. */
|
|
1146
1102
|
function resolveToFunction(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
1147
1103
|
const expression = Extract.unwrap(node);
|
|
1148
1104
|
if (Check.isFunction(expression)) return expression;
|
|
@@ -1222,22 +1178,11 @@ function createFactCollector$6() {
|
|
|
1222
1178
|
|
|
1223
1179
|
//#endregion
|
|
1224
1180
|
//#region src/rules/globals/origins.ts
|
|
1225
|
-
/**
|
|
1226
|
-
* Return whether an identifier is an unresolved global or is declared in the
|
|
1227
|
-
* global/module scope.
|
|
1228
|
-
*/
|
|
1229
1181
|
function isGlobalVariable(context, node) {
|
|
1230
1182
|
const variable = findVariable(context.sourceCode.getScope(node), node);
|
|
1231
1183
|
if (variable == null || variable.defs.length === 0) return true;
|
|
1232
1184
|
return variable.scope.type === ScopeType.global || variable.scope.type === ScopeType.module;
|
|
1233
1185
|
}
|
|
1234
|
-
/**
|
|
1235
|
-
* Resolve an object expression to the global/module binding it aliases.
|
|
1236
|
-
*
|
|
1237
|
-
* This intentionally follows only stable `const alias = value` declarations.
|
|
1238
|
-
* It gives the rule useful Alias effects without pretending to perform full
|
|
1239
|
-
* control-flow analysis for reassigned locals.
|
|
1240
|
-
*/
|
|
1241
1186
|
function resolveGlobalOrigin(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
1242
1187
|
const expression = Extract.unwrap(node);
|
|
1243
1188
|
if (seen.has(expression)) return null;
|
|
@@ -1257,11 +1202,6 @@ function resolveGlobalOrigin(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
|
1257
1202
|
|
|
1258
1203
|
//#endregion
|
|
1259
1204
|
//#region src/rules/globals/effects.ts
|
|
1260
|
-
/**
|
|
1261
|
-
* Classify the collected writes and mutating method calls, keeping only the
|
|
1262
|
-
* ones that reach a global/module binding, grouped by the function that
|
|
1263
|
-
* performs them.
|
|
1264
|
-
*/
|
|
1265
1205
|
function inferGlobalMutations(context, facts) {
|
|
1266
1206
|
const directEffects = /* @__PURE__ */ new Map();
|
|
1267
1207
|
function pushEffect(enclosingFunction, effect) {
|
|
@@ -1300,7 +1240,6 @@ function inferGlobalMutations(context, facts) {
|
|
|
1300
1240
|
}
|
|
1301
1241
|
return directEffects;
|
|
1302
1242
|
}
|
|
1303
|
-
/** Resolve the collected call edges into a function-to-function call graph. */
|
|
1304
1243
|
function inferCallGraph$1(context, callEdges) {
|
|
1305
1244
|
const callGraph = /* @__PURE__ */ new Map();
|
|
1306
1245
|
for (const edge of callEdges) {
|
|
@@ -1312,11 +1251,6 @@ function inferCallGraph$1(context, callEdges) {
|
|
|
1312
1251
|
}
|
|
1313
1252
|
return callGraph;
|
|
1314
1253
|
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Like the SPEC's function signatures, these summaries keep creation of an
|
|
1317
|
-
* effect separate from applying it in a component or hook render: walk the
|
|
1318
|
-
* call graph from each render function and gather every reachable effect once.
|
|
1319
|
-
*/
|
|
1320
1254
|
function collectReachableEffects(renderFunctions, directEffects, callGraph) {
|
|
1321
1255
|
const visited = /* @__PURE__ */ new Set();
|
|
1322
1256
|
const reported = /* @__PURE__ */ new Set();
|
|
@@ -1377,12 +1311,6 @@ function create$49(context) {
|
|
|
1377
1311
|
|
|
1378
1312
|
//#endregion
|
|
1379
1313
|
//#region src/rules/immutability/helpers.ts
|
|
1380
|
-
/**
|
|
1381
|
-
* Methods that mutate their receiver in place.
|
|
1382
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
|
|
1383
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
|
1384
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
|
|
1385
|
-
*/
|
|
1386
1314
|
const KNOWN_MUTATING_METHODS = /* @__PURE__ */ new Set([
|
|
1387
1315
|
"add",
|
|
1388
1316
|
"clear",
|
|
@@ -1398,9 +1326,6 @@ const KNOWN_MUTATING_METHODS = /* @__PURE__ */ new Set([
|
|
|
1398
1326
|
"splice",
|
|
1399
1327
|
"unshift"
|
|
1400
1328
|
]);
|
|
1401
|
-
/**
|
|
1402
|
-
* Known mutating hooks.
|
|
1403
|
-
*/
|
|
1404
1329
|
const KNOWN_MUTATING_HOOKS = /* @__PURE__ */ new Set([
|
|
1405
1330
|
"useHistory",
|
|
1406
1331
|
"useNavigate",
|
|
@@ -1571,17 +1496,6 @@ function createFactCollector$5() {
|
|
|
1571
1496
|
|
|
1572
1497
|
//#endregion
|
|
1573
1498
|
//#region src/rules/immutability/origins.ts
|
|
1574
|
-
/**
|
|
1575
|
-
* Classify whether a variable ultimately holds a value that must be treated as
|
|
1576
|
-
* immutable: a component's props, a state value returned from `useState`-like or
|
|
1577
|
-
* `useReducer` calls, a shallow copy (spread literal) of either, or a `for...of`
|
|
1578
|
-
* iterator variable whose iterated collection resolves to one of those.
|
|
1579
|
-
* @param context The rule context.
|
|
1580
|
-
* @param variable The variable to classify.
|
|
1581
|
-
* @param components The confirmed function component nodes in the file.
|
|
1582
|
-
* @param seen Variables already visited during spread/iterator recursion.
|
|
1583
|
-
* @returns The frozen origin, or `null` when the variable is not derived from one.
|
|
1584
|
-
*/
|
|
1585
1499
|
function classifyFrozenOrigin(context, variable, components, seen = /* @__PURE__ */ new Set()) {
|
|
1586
1500
|
if (seen.has(variable)) return null;
|
|
1587
1501
|
seen.add(variable);
|
|
@@ -1835,12 +1749,6 @@ function create$47(context) {
|
|
|
1835
1749
|
|
|
1836
1750
|
//#endregion
|
|
1837
1751
|
//#region src/rules/no-array-index-key/lib.ts
|
|
1838
|
-
/**
|
|
1839
|
-
* Iterator-like methods that pass the item's index to their callback,
|
|
1840
|
-
* mapped to the position of the index parameter in the callback's parameter list.
|
|
1841
|
-
* `map` and `forEach` also cover `Children.map` and `Children.forEach`,
|
|
1842
|
-
* whose callback is the second argument instead of the first.
|
|
1843
|
-
*/
|
|
1844
1752
|
const INDEX_PARAM_POSITIONS$1 = /* @__PURE__ */ new Map([
|
|
1845
1753
|
["every", 1],
|
|
1846
1754
|
["filter", 1],
|
|
@@ -1855,13 +1763,6 @@ const INDEX_PARAM_POSITIONS$1 = /* @__PURE__ */ new Map([
|
|
|
1855
1763
|
["reduceRight", 2],
|
|
1856
1764
|
["some", 1]
|
|
1857
1765
|
]);
|
|
1858
|
-
/**
|
|
1859
|
-
* Checks whether an identifier is a reference to the index parameter of an
|
|
1860
|
-
* iterator-like method's callback (e.g. `i` in `items.map((item, i) => ...)`).
|
|
1861
|
-
* @param context The ESLint rule context.
|
|
1862
|
-
* @param node The identifier to check.
|
|
1863
|
-
* @returns `true` if the identifier resolves to an array index parameter.
|
|
1864
|
-
*/
|
|
1865
1766
|
function isArrayIndexReference(context, node) {
|
|
1866
1767
|
const def = findVariable(context.sourceCode.getScope(node), node)?.defs.at(0);
|
|
1867
1768
|
if (def == null || def.type !== DefinitionType.Parameter) return false;
|
|
@@ -1882,12 +1783,6 @@ function isArrayIndexReference(context, node) {
|
|
|
1882
1783
|
if (param == null) return false;
|
|
1883
1784
|
return param === def.name || param.type === AST_NODE_TYPES.AssignmentPattern && param.left === def.name;
|
|
1884
1785
|
}
|
|
1885
|
-
/**
|
|
1886
|
-
* Recursively collects all identifiers from a binary expression.
|
|
1887
|
-
* e.g., for `a + b + c`, it returns identifiers for a, b, and c.
|
|
1888
|
-
* @param side The binary expression (or one of its sides) to collect from.
|
|
1889
|
-
* @returns The identifiers found in the expression.
|
|
1890
|
-
*/
|
|
1891
1786
|
function getIdentifiersFromBinaryExpression(side) {
|
|
1892
1787
|
if (Check.isIdentifier(side)) return [side];
|
|
1893
1788
|
if (side.type === AST_NODE_TYPES.BinaryExpression) return [...getIdentifiersFromBinaryExpression(side.left), ...getIdentifiersFromBinaryExpression(side.right)];
|
|
@@ -1897,7 +1792,6 @@ function getIdentifiersFromBinaryExpression(side) {
|
|
|
1897
1792
|
//#endregion
|
|
1898
1793
|
//#region src/rules/no-array-index-key/no-array-index-key.ts
|
|
1899
1794
|
const RULE_NAME$46 = "no-array-index-key";
|
|
1900
|
-
/** Global functions that convert their first argument while preserving its identity as a key. */
|
|
1901
1795
|
const COERCION_FUNCTIONS = /* @__PURE__ */ new Set(["Number", "String"]);
|
|
1902
1796
|
var no_array_index_key_default = createRule({
|
|
1903
1797
|
meta: {
|
|
@@ -1920,12 +1814,6 @@ function create$46(context) {
|
|
|
1920
1814
|
if (!core.isCloneElementCall(context, node)) return null;
|
|
1921
1815
|
return props?.type === AST_NODE_TYPES.ObjectExpression ? props : null;
|
|
1922
1816
|
}
|
|
1923
|
-
/**
|
|
1924
|
-
* Checks an expression used as a 'key' value, recursing into the branches
|
|
1925
|
-
* it may evaluate to and reporting every array index it is derived from.
|
|
1926
|
-
* @param node The key value expression to check.
|
|
1927
|
-
* @returns The report descriptors for the violations found.
|
|
1928
|
-
*/
|
|
1929
1817
|
function visitKeyExpression(node) {
|
|
1930
1818
|
switch (node.type) {
|
|
1931
1819
|
case AST_NODE_TYPES.Identifier: return isArrayIndex(node) ? [{
|
|
@@ -2094,44 +1982,6 @@ function create$41(context) {
|
|
|
2094
1982
|
|
|
2095
1983
|
//#endregion
|
|
2096
1984
|
//#region ../../.pkgs/eff/dist/index.js
|
|
2097
|
-
/**
|
|
2098
|
-
* Applies a `pipe` method's variadic arguments to an initial value from left
|
|
2099
|
-
* to right.
|
|
2100
|
-
*
|
|
2101
|
-
* **When to use**
|
|
2102
|
-
*
|
|
2103
|
-
* Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
|
|
2104
|
-
* object.
|
|
2105
|
-
*
|
|
2106
|
-
* **Details**
|
|
2107
|
-
*
|
|
2108
|
-
* This helper is intended for implementing `Pipeable.pipe` methods that
|
|
2109
|
-
* receive JavaScript's `arguments` object. With no functions it returns the
|
|
2110
|
-
* original value; otherwise it feeds each result into the next function.
|
|
2111
|
-
*
|
|
2112
|
-
* **Example** (Implementing a pipe method)
|
|
2113
|
-
*
|
|
2114
|
-
* ```ts
|
|
2115
|
-
* import { Pipeable } from "effect"
|
|
2116
|
-
*
|
|
2117
|
-
* class NumberBox {
|
|
2118
|
-
* constructor(readonly value: number) {}
|
|
2119
|
-
*
|
|
2120
|
-
* pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
|
|
2121
|
-
* return Pipeable.pipeArguments(this.value, arguments) as number
|
|
2122
|
-
* }
|
|
2123
|
-
* }
|
|
2124
|
-
*
|
|
2125
|
-
* const result = new NumberBox(5).pipe(
|
|
2126
|
-
* (n) => n + 2,
|
|
2127
|
-
* (n) => n * 3
|
|
2128
|
-
* )
|
|
2129
|
-
* result // => 21
|
|
2130
|
-
* ```
|
|
2131
|
-
*
|
|
2132
|
-
* @category combinators
|
|
2133
|
-
* @since 2.0.0
|
|
2134
|
-
*/
|
|
2135
1985
|
const pipeArguments = (self, args) => {
|
|
2136
1986
|
switch (args.length) {
|
|
2137
1987
|
case 0: return self;
|
|
@@ -2151,100 +2001,14 @@ const pipeArguments = (self, args) => {
|
|
|
2151
2001
|
}
|
|
2152
2002
|
}
|
|
2153
2003
|
};
|
|
2154
|
-
/**
|
|
2155
|
-
* Reusable prototype that implements `Pipeable.pipe`.
|
|
2156
|
-
*
|
|
2157
|
-
* **When to use**
|
|
2158
|
-
*
|
|
2159
|
-
* Use when classes or object prototypes can reuse this value when they need the
|
|
2160
|
-
* standard pipe implementation backed by `pipeArguments`.
|
|
2161
|
-
*
|
|
2162
|
-
* @category prototypes
|
|
2163
|
-
* @since 3.15.0
|
|
2164
|
-
*/
|
|
2165
2004
|
const Prototype = { pipe() {
|
|
2166
2005
|
return pipeArguments(this, arguments);
|
|
2167
2006
|
} };
|
|
2168
|
-
/**
|
|
2169
|
-
* Provides a base constructor whose instances implement the standard `Pipeable.pipe`
|
|
2170
|
-
* method.
|
|
2171
|
-
*
|
|
2172
|
-
* **When to use**
|
|
2173
|
-
*
|
|
2174
|
-
* Use when you need to define a class that supports Effect-style method
|
|
2175
|
-
* chaining through `.pipe(...)`.
|
|
2176
|
-
*
|
|
2177
|
-
* @category constructors
|
|
2178
|
-
* @since 3.15.0
|
|
2179
|
-
*/
|
|
2180
2007
|
const Class = (function() {
|
|
2181
2008
|
function PipeableBase() {}
|
|
2182
2009
|
PipeableBase.prototype = Prototype;
|
|
2183
2010
|
return PipeableBase;
|
|
2184
2011
|
})();
|
|
2185
|
-
/**
|
|
2186
|
-
* Creates a function that can be called in data-first style or data-last
|
|
2187
|
-
* (`pipe`-friendly) style.
|
|
2188
|
-
*
|
|
2189
|
-
* **When to use**
|
|
2190
|
-
*
|
|
2191
|
-
* Use to expose one implementation through both direct and `pipe`-friendly
|
|
2192
|
-
* call styles.
|
|
2193
|
-
*
|
|
2194
|
-
* **Details**
|
|
2195
|
-
*
|
|
2196
|
-
* Pass either the arity of the uncurried function or a predicate that decides
|
|
2197
|
-
* whether the current call is data-first. Arity is the common case. Use a
|
|
2198
|
-
* predicate when optional arguments make arity ambiguous.
|
|
2199
|
-
*
|
|
2200
|
-
* **Example** (Selecting data-first or data-last style by arity)
|
|
2201
|
-
*
|
|
2202
|
-
* ```ts
|
|
2203
|
-
* import { Function, pipe } from "effect"
|
|
2204
|
-
*
|
|
2205
|
-
* const sum = Function.dual<
|
|
2206
|
-
* (that: number) => (self: number) => number,
|
|
2207
|
-
* (self: number, that: number) => number
|
|
2208
|
-
* >(2, (self, that) => self + that)
|
|
2209
|
-
*
|
|
2210
|
-
* sum(2, 3) // => 5
|
|
2211
|
-
* pipe(2, sum(3)) // => 5
|
|
2212
|
-
* ```
|
|
2213
|
-
*
|
|
2214
|
-
* **Example** (Defining overloads with call signatures)
|
|
2215
|
-
*
|
|
2216
|
-
* ```ts
|
|
2217
|
-
* import { Function, pipe } from "effect"
|
|
2218
|
-
*
|
|
2219
|
-
* const sum: {
|
|
2220
|
-
* (that: number): (self: number) => number
|
|
2221
|
-
* (self: number, that: number): number
|
|
2222
|
-
* } = Function.dual(2, (self: number, that: number): number => self + that)
|
|
2223
|
-
*
|
|
2224
|
-
* sum(2, 3) // => 5
|
|
2225
|
-
* pipe(2, sum(3)) // => 5
|
|
2226
|
-
* ```
|
|
2227
|
-
*
|
|
2228
|
-
* **Example** (Selecting data-first or data-last style with a predicate)
|
|
2229
|
-
*
|
|
2230
|
-
* ```ts
|
|
2231
|
-
* import { Function, pipe } from "effect"
|
|
2232
|
-
*
|
|
2233
|
-
* const sum = Function.dual<
|
|
2234
|
-
* (that: number) => (self: number) => number,
|
|
2235
|
-
* (self: number, that: number) => number
|
|
2236
|
-
* >(
|
|
2237
|
-
* (args) => args.length === 2,
|
|
2238
|
-
* (self, that) => self + that
|
|
2239
|
-
* )
|
|
2240
|
-
*
|
|
2241
|
-
* sum(2, 3) // => 5
|
|
2242
|
-
* pipe(2, sum(3)) // => 5
|
|
2243
|
-
* ```
|
|
2244
|
-
*
|
|
2245
|
-
* @category combinators
|
|
2246
|
-
* @since 2.0.0
|
|
2247
|
-
*/
|
|
2248
2012
|
const dual = function(arity, body) {
|
|
2249
2013
|
if (typeof arity === "function") return function() {
|
|
2250
2014
|
return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
|
|
@@ -2273,196 +2037,14 @@ const dual = function(arity, body) {
|
|
|
2273
2037
|
};
|
|
2274
2038
|
}
|
|
2275
2039
|
};
|
|
2276
|
-
/**
|
|
2277
|
-
* Returns its input argument unchanged.
|
|
2278
|
-
*
|
|
2279
|
-
* **When to use**
|
|
2280
|
-
*
|
|
2281
|
-
* Use to return a value unchanged where a function is required.
|
|
2282
|
-
*
|
|
2283
|
-
* **Example** (Returning the same value)
|
|
2284
|
-
*
|
|
2285
|
-
* ```ts
|
|
2286
|
-
* import { identity } from "effect"
|
|
2287
|
-
*
|
|
2288
|
-
* identity(5) // => 5
|
|
2289
|
-
* ```
|
|
2290
|
-
*
|
|
2291
|
-
* @category combinators
|
|
2292
|
-
* @since 2.0.0
|
|
2293
|
-
*/
|
|
2294
2040
|
const identity = (a) => a;
|
|
2295
|
-
/**
|
|
2296
|
-
* Returns the input value with a different static type.
|
|
2297
|
-
*
|
|
2298
|
-
* **When to use**
|
|
2299
|
-
*
|
|
2300
|
-
* Use when you need an explicit type-level cast and accept that the value is
|
|
2301
|
-
* returned unchanged at runtime.
|
|
2302
|
-
*
|
|
2303
|
-
* **Gotchas**
|
|
2304
|
-
*
|
|
2305
|
-
* This is a type-level cast only; it performs no runtime validation or
|
|
2306
|
-
* conversion.
|
|
2307
|
-
*
|
|
2308
|
-
* @see {@link satisfies} for checking assignability without changing the resulting type
|
|
2309
|
-
*
|
|
2310
|
-
* @category utility types
|
|
2311
|
-
* @since 4.0.0
|
|
2312
|
-
*/
|
|
2313
2041
|
const cast = identity;
|
|
2314
|
-
/**
|
|
2315
|
-
* Creates a zero-argument function that always returns the provided value.
|
|
2316
|
-
*
|
|
2317
|
-
* **When to use**
|
|
2318
|
-
*
|
|
2319
|
-
* Use when you need a thunk or callback that returns the same value on every
|
|
2320
|
-
* invocation.
|
|
2321
|
-
*
|
|
2322
|
-
* **Example** (Creating a constant thunk)
|
|
2323
|
-
*
|
|
2324
|
-
* ```ts
|
|
2325
|
-
* import { Function } from "effect"
|
|
2326
|
-
*
|
|
2327
|
-
* const constNull = Function.constant(null)
|
|
2328
|
-
*
|
|
2329
|
-
* constNull() // => null
|
|
2330
|
-
* constNull() // => null
|
|
2331
|
-
* ```
|
|
2332
|
-
*
|
|
2333
|
-
* @category constructors
|
|
2334
|
-
* @since 2.0.0
|
|
2335
|
-
*/
|
|
2336
2042
|
const constant = (value) => () => value;
|
|
2337
|
-
/**
|
|
2338
|
-
* Returns `true` when called.
|
|
2339
|
-
*
|
|
2340
|
-
* **When to use**
|
|
2341
|
-
*
|
|
2342
|
-
* Use when you need a thunk that returns `true` on every invocation.
|
|
2343
|
-
*
|
|
2344
|
-
* **Example** (Returning true from a thunk)
|
|
2345
|
-
*
|
|
2346
|
-
* ```ts
|
|
2347
|
-
* import { Function } from "effect"
|
|
2348
|
-
*
|
|
2349
|
-
* Function.constTrue() // => true
|
|
2350
|
-
* ```
|
|
2351
|
-
*
|
|
2352
|
-
* @category constants
|
|
2353
|
-
* @since 2.0.0
|
|
2354
|
-
*/
|
|
2355
2043
|
const constTrue = constant(true);
|
|
2356
|
-
/**
|
|
2357
|
-
* Returns `false` when called.
|
|
2358
|
-
*
|
|
2359
|
-
* **When to use**
|
|
2360
|
-
*
|
|
2361
|
-
* Use when you need a thunk that returns `false` on every invocation.
|
|
2362
|
-
*
|
|
2363
|
-
* **Example** (Returning false from a thunk)
|
|
2364
|
-
*
|
|
2365
|
-
* ```ts
|
|
2366
|
-
* import { Function } from "effect"
|
|
2367
|
-
*
|
|
2368
|
-
* Function.constFalse() // => false
|
|
2369
|
-
* ```
|
|
2370
|
-
*
|
|
2371
|
-
* @category constants
|
|
2372
|
-
* @since 2.0.0
|
|
2373
|
-
*/
|
|
2374
2044
|
const constFalse = constant(false);
|
|
2375
|
-
/**
|
|
2376
|
-
* Returns `null` when called.
|
|
2377
|
-
*
|
|
2378
|
-
* **When to use**
|
|
2379
|
-
*
|
|
2380
|
-
* Use when you need a thunk that returns `null` on every invocation.
|
|
2381
|
-
*
|
|
2382
|
-
* **Example** (Returning null from a thunk)
|
|
2383
|
-
*
|
|
2384
|
-
* ```ts
|
|
2385
|
-
* import { Function } from "effect"
|
|
2386
|
-
*
|
|
2387
|
-
* Function.constNull() // => null
|
|
2388
|
-
* ```
|
|
2389
|
-
*
|
|
2390
|
-
* @category constants
|
|
2391
|
-
* @since 2.0.0
|
|
2392
|
-
*/
|
|
2393
2045
|
const constNull = constant(null);
|
|
2394
|
-
/**
|
|
2395
|
-
* Returns `undefined` when called.
|
|
2396
|
-
*
|
|
2397
|
-
* **When to use**
|
|
2398
|
-
*
|
|
2399
|
-
* Use when you need a thunk that returns `undefined` on every invocation.
|
|
2400
|
-
*
|
|
2401
|
-
* **Example** (Returning undefined from a thunk)
|
|
2402
|
-
*
|
|
2403
|
-
* ```ts
|
|
2404
|
-
* import { Function } from "effect"
|
|
2405
|
-
*
|
|
2406
|
-
* Function.constUndefined() // => undefined
|
|
2407
|
-
* ```
|
|
2408
|
-
*
|
|
2409
|
-
* @category constants
|
|
2410
|
-
* @since 2.0.0
|
|
2411
|
-
*/
|
|
2412
2046
|
const constUndefined = constant(void 0);
|
|
2413
|
-
/**
|
|
2414
|
-
* Composes two functions, `ab` and `bc` into a single function that takes in an argument `a` of type `A` and returns a result of type `C`.
|
|
2415
|
-
* The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
|
|
2416
|
-
*
|
|
2417
|
-
* **When to use**
|
|
2418
|
-
*
|
|
2419
|
-
* Use to compose exactly two unary functions into a reusable unary function.
|
|
2420
|
-
*
|
|
2421
|
-
* **Example** (Composing two functions)
|
|
2422
|
-
*
|
|
2423
|
-
* ```ts
|
|
2424
|
-
* import { Function } from "effect"
|
|
2425
|
-
*
|
|
2426
|
-
* const increment = (n: number) => n + 1
|
|
2427
|
-
* const square = (n: number) => n * n
|
|
2428
|
-
*
|
|
2429
|
-
* Function.compose(increment, square)(2) // => 9
|
|
2430
|
-
* ```
|
|
2431
|
-
*
|
|
2432
|
-
* @see {@link flow} for composing a left-to-right sequence of functions
|
|
2433
|
-
* @see {@link pipe} for applying a value through a left-to-right sequence immediately
|
|
2434
|
-
*
|
|
2435
|
-
* @category combinators
|
|
2436
|
-
* @since 2.0.0
|
|
2437
|
-
*/
|
|
2438
2047
|
const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
|
|
2439
|
-
/**
|
|
2440
|
-
* Marks an impossible branch by accepting a `never` value and returning any
|
|
2441
|
-
* type.
|
|
2442
|
-
*
|
|
2443
|
-
* **When to use**
|
|
2444
|
-
*
|
|
2445
|
-
* Use when you need a return value in a branch that exhaustive checks prove
|
|
2446
|
-
* cannot be reached.
|
|
2447
|
-
*
|
|
2448
|
-
* **Gotchas**
|
|
2449
|
-
*
|
|
2450
|
-
* Calling `absurd` throws, because a value of type `never` should be
|
|
2451
|
-
* impossible at runtime.
|
|
2452
|
-
*
|
|
2453
|
-
* **Example** (Handling impossible values)
|
|
2454
|
-
*
|
|
2455
|
-
* ```ts
|
|
2456
|
-
* import { absurd } from "effect"
|
|
2457
|
-
*
|
|
2458
|
-
* const handleNever = (value: never) => {
|
|
2459
|
-
* return absurd(value) // This will throw an error if called
|
|
2460
|
-
* }
|
|
2461
|
-
* ```
|
|
2462
|
-
*
|
|
2463
|
-
* @category utility types
|
|
2464
|
-
* @since 2.0.0
|
|
2465
|
-
*/
|
|
2466
2048
|
const absurd = (_) => {
|
|
2467
2049
|
throw new Error("Called `absurd` function which should be uncallable");
|
|
2468
2050
|
};
|
|
@@ -2495,372 +2077,26 @@ function flow(ab, bc, cd, de, ef, fg, gh, hi, ij) {
|
|
|
2495
2077
|
};
|
|
2496
2078
|
}
|
|
2497
2079
|
}
|
|
2498
|
-
/**
|
|
2499
|
-
* Creates a compile-time placeholder for a value of any type.
|
|
2500
|
-
*
|
|
2501
|
-
* **When to use**
|
|
2502
|
-
*
|
|
2503
|
-
* Use as a temporary typed placeholder while developing incomplete code.
|
|
2504
|
-
*
|
|
2505
|
-
* **Gotchas**
|
|
2506
|
-
*
|
|
2507
|
-
* `hole` is intended for temporary development use. If the placeholder is
|
|
2508
|
-
* evaluated at runtime, it throws.
|
|
2509
|
-
*
|
|
2510
|
-
* **Example** (Creating a development placeholder)
|
|
2511
|
-
*
|
|
2512
|
-
* ```ts
|
|
2513
|
-
* import { hole } from "effect"
|
|
2514
|
-
*
|
|
2515
|
-
* // Intentionally not called: `hole` throws if the placeholder is evaluated.
|
|
2516
|
-
* const buildUser = (id: number): { readonly id: number; readonly name: string } => ({
|
|
2517
|
-
* id,
|
|
2518
|
-
* name: hole<string>()
|
|
2519
|
-
* })
|
|
2520
|
-
*
|
|
2521
|
-
* ```
|
|
2522
|
-
*
|
|
2523
|
-
* @category utility types
|
|
2524
|
-
* @since 2.0.0
|
|
2525
|
-
*/
|
|
2526
2080
|
const hole = cast(absurd);
|
|
2527
2081
|
function not(predicate) {
|
|
2528
2082
|
return (data) => !predicate(data);
|
|
2529
2083
|
}
|
|
2530
|
-
/**
|
|
2531
|
-
* Creates a predicate that returns `true` only if both predicates are `true`.
|
|
2532
|
-
*
|
|
2533
|
-
* **When to use**
|
|
2534
|
-
*
|
|
2535
|
-
* Use when you want to combine `Predicate`s with AND, accepting values that
|
|
2536
|
-
* satisfy multiple conditions, including refinements that narrow to an
|
|
2537
|
-
* intersection.
|
|
2538
|
-
*
|
|
2539
|
-
* **Details**
|
|
2540
|
-
*
|
|
2541
|
-
* Evaluation short-circuits on the first `false`. For refinements, the output
|
|
2542
|
-
* type is an intersection.
|
|
2543
|
-
*
|
|
2544
|
-
* **Example** (Checking both conditions)
|
|
2545
|
-
*
|
|
2546
|
-
* ```ts
|
|
2547
|
-
* import { Predicate } from "effect"
|
|
2548
|
-
*
|
|
2549
|
-
* const hasAAndB = Predicate.and(
|
|
2550
|
-
* Predicate.hasProperty("a"),
|
|
2551
|
-
* Predicate.hasProperty("b")
|
|
2552
|
-
* )
|
|
2553
|
-
*
|
|
2554
|
-
* const input: unknown = JSON.parse(`{"a":1,"b":"ok"}`)
|
|
2555
|
-
* if (hasAAndB(input)) {
|
|
2556
|
-
* // input has both properties at this point
|
|
2557
|
-
* const a = input.a
|
|
2558
|
-
* const b = input.b
|
|
2559
|
-
*
|
|
2560
|
-
* const values = [a, b] // => [1, "ok"]
|
|
2561
|
-
* }
|
|
2562
|
-
* ```
|
|
2563
|
-
*
|
|
2564
|
-
* @see {@link or}
|
|
2565
|
-
* @see {@link not}
|
|
2566
|
-
* @category combinators
|
|
2567
|
-
* @since 2.0.0
|
|
2568
|
-
*/
|
|
2569
2084
|
const and = dual(2, (a, b) => (data) => a(data) && b(data));
|
|
2570
|
-
/**
|
|
2571
|
-
* Creates a predicate that returns `true` if either predicate is `true`.
|
|
2572
|
-
*
|
|
2573
|
-
* **When to use**
|
|
2574
|
-
*
|
|
2575
|
-
* Use when you want to combine `Predicate`s with OR, accepting values that
|
|
2576
|
-
* satisfy at least one condition, including refinements that narrow to a union.
|
|
2577
|
-
*
|
|
2578
|
-
* **Details**
|
|
2579
|
-
*
|
|
2580
|
-
* Evaluation short-circuits on the first `true`. For refinements, the output
|
|
2581
|
-
* type is a union.
|
|
2582
|
-
*
|
|
2583
|
-
* **Example** (Checking either condition)
|
|
2584
|
-
*
|
|
2585
|
-
* ```ts
|
|
2586
|
-
* import { Predicate } from "effect"
|
|
2587
|
-
*
|
|
2588
|
-
* const isStringOrNumber = Predicate.or(Predicate.isString, Predicate.isNumber)
|
|
2589
|
-
*
|
|
2590
|
-
* isStringOrNumber("a") // => true
|
|
2591
|
-
* ```
|
|
2592
|
-
*
|
|
2593
|
-
* @see {@link and}
|
|
2594
|
-
* @see {@link xor}
|
|
2595
|
-
* @category combinators
|
|
2596
|
-
* @since 2.0.0
|
|
2597
|
-
*/
|
|
2598
2085
|
const or = dual(2, (a, b) => (data) => a(data) || b(data));
|
|
2599
|
-
/**
|
|
2600
|
-
* Creates a predicate that returns `true` if exactly one predicate is `true`.
|
|
2601
|
-
*
|
|
2602
|
-
* **When to use**
|
|
2603
|
-
*
|
|
2604
|
-
* Use when you want to combine two `Predicate`s with exclusive-or semantics.
|
|
2605
|
-
*
|
|
2606
|
-
* **Details**
|
|
2607
|
-
*
|
|
2608
|
-
* Returns `true` when results differ.
|
|
2609
|
-
*
|
|
2610
|
-
* **Example** (Checking exclusive-or conditions)
|
|
2611
|
-
*
|
|
2612
|
-
* ```ts
|
|
2613
|
-
* import { Predicate } from "effect"
|
|
2614
|
-
*
|
|
2615
|
-
* const isEven = (n: number) => n % 2 === 0
|
|
2616
|
-
* const isPositive = (n: number) => n > 0
|
|
2617
|
-
* const either = Predicate.xor(isEven, isPositive)
|
|
2618
|
-
*
|
|
2619
|
-
* either(-2) // => true
|
|
2620
|
-
* ```
|
|
2621
|
-
*
|
|
2622
|
-
* @see {@link or}
|
|
2623
|
-
* @see {@link and}
|
|
2624
|
-
* @category combinators
|
|
2625
|
-
* @since 2.0.0
|
|
2626
|
-
*/
|
|
2627
2086
|
const xor = dual(2, (a, b) => (data) => a(data) !== b(data));
|
|
2628
|
-
/**
|
|
2629
|
-
* Creates a predicate that returns `true` when both predicates agree.
|
|
2630
|
-
*
|
|
2631
|
-
* **When to use**
|
|
2632
|
-
*
|
|
2633
|
-
* Use when you want to check equivalence of two `Predicate`s.
|
|
2634
|
-
*
|
|
2635
|
-
* **Details**
|
|
2636
|
-
*
|
|
2637
|
-
* Returns `true` when both results are equal.
|
|
2638
|
-
*
|
|
2639
|
-
* **Example** (Defining equivalence)
|
|
2640
|
-
*
|
|
2641
|
-
* ```ts
|
|
2642
|
-
* import { Predicate } from "effect"
|
|
2643
|
-
*
|
|
2644
|
-
* const isEven = (n: number) => n % 2 === 0
|
|
2645
|
-
* const same = Predicate.eqv(isEven, isEven)
|
|
2646
|
-
*
|
|
2647
|
-
* same(3) // => true
|
|
2648
|
-
* ```
|
|
2649
|
-
*
|
|
2650
|
-
* @see {@link xor}
|
|
2651
|
-
* @category combinators
|
|
2652
|
-
* @since 2.0.0
|
|
2653
|
-
*/
|
|
2654
2087
|
const eqv = dual(2, (a, b) => (data) => a(data) === b(data));
|
|
2655
|
-
/**
|
|
2656
|
-
* Creates a predicate representing logical implication: if `antecedent`, then `consequent`.
|
|
2657
|
-
*
|
|
2658
|
-
* **When to use**
|
|
2659
|
-
*
|
|
2660
|
-
* Use when you need to encode logical implication between `Predicate` rules,
|
|
2661
|
-
* where one rule only applies when a precondition holds.
|
|
2662
|
-
*
|
|
2663
|
-
* **Details**
|
|
2664
|
-
*
|
|
2665
|
-
* Models constraints like "if A then B" and returns `true` when the antecedent
|
|
2666
|
-
* is `false`.
|
|
2667
|
-
*
|
|
2668
|
-
* **Example** (Checking implication)
|
|
2669
|
-
*
|
|
2670
|
-
* ```ts
|
|
2671
|
-
* import { Predicate } from "effect"
|
|
2672
|
-
*
|
|
2673
|
-
* const isAdult = (age: number) => age >= 18
|
|
2674
|
-
* const canVote = (age: number) => age >= 18
|
|
2675
|
-
* const implies = Predicate.implies(isAdult, canVote)
|
|
2676
|
-
*
|
|
2677
|
-
* implies(16) // => true
|
|
2678
|
-
* ```
|
|
2679
|
-
*
|
|
2680
|
-
* @see {@link and}
|
|
2681
|
-
* @see {@link or}
|
|
2682
|
-
* @category combinators
|
|
2683
|
-
* @since 2.0.0
|
|
2684
|
-
*/
|
|
2685
2088
|
const implies = dual(2, (antecedent, consequent) => (data) => !antecedent(data) || consequent(data));
|
|
2686
|
-
/**
|
|
2687
|
-
* Creates a predicate that returns `true` when neither predicate is `true`.
|
|
2688
|
-
*
|
|
2689
|
-
* **When to use**
|
|
2690
|
-
*
|
|
2691
|
-
* Use when you want to combine two `Predicate`s with logical NOR semantics.
|
|
2692
|
-
*
|
|
2693
|
-
* **Details**
|
|
2694
|
-
*
|
|
2695
|
-
* Returns the negation of `or`.
|
|
2696
|
-
*
|
|
2697
|
-
* **Example** (Checking NOR conditions)
|
|
2698
|
-
*
|
|
2699
|
-
* ```ts
|
|
2700
|
-
* import { Predicate } from "effect"
|
|
2701
|
-
*
|
|
2702
|
-
* const neither = Predicate.nor(Predicate.isString, Predicate.isNumber)
|
|
2703
|
-
*
|
|
2704
|
-
* neither(true) // => true
|
|
2705
|
-
* ```
|
|
2706
|
-
*
|
|
2707
|
-
* @see {@link or}
|
|
2708
|
-
* @see {@link not}
|
|
2709
|
-
* @category combinators
|
|
2710
|
-
* @since 2.0.0
|
|
2711
|
-
*/
|
|
2712
2089
|
const nor = dual(2, (a, b) => (data) => !a(data) && !b(data));
|
|
2713
|
-
/**
|
|
2714
|
-
* Creates a predicate that returns `true` unless both predicates are `true`.
|
|
2715
|
-
*
|
|
2716
|
-
* **When to use**
|
|
2717
|
-
*
|
|
2718
|
-
* Use when you want to combine two `Predicate`s with logical NAND semantics.
|
|
2719
|
-
*
|
|
2720
|
-
* **Details**
|
|
2721
|
-
*
|
|
2722
|
-
* Returns the negation of `and`.
|
|
2723
|
-
*
|
|
2724
|
-
* **Example** (Checking NAND conditions)
|
|
2725
|
-
*
|
|
2726
|
-
* ```ts
|
|
2727
|
-
* import { Predicate } from "effect"
|
|
2728
|
-
*
|
|
2729
|
-
* const notBoth = Predicate.nand(Predicate.isString, Predicate.isNumber)
|
|
2730
|
-
*
|
|
2731
|
-
* notBoth("a") // => true
|
|
2732
|
-
* ```
|
|
2733
|
-
*
|
|
2734
|
-
* @see {@link and}
|
|
2735
|
-
* @see {@link not}
|
|
2736
|
-
* @category combinators
|
|
2737
|
-
* @since 2.0.0
|
|
2738
|
-
*/
|
|
2739
2090
|
const nand = dual(2, (a, b) => (data) => !a(data) || !b(data));
|
|
2740
|
-
/**
|
|
2741
|
-
* Checks whether a value is a `string`.
|
|
2742
|
-
*
|
|
2743
|
-
* **When to use**
|
|
2744
|
-
*
|
|
2745
|
-
* Use when you need a `Predicate` guard to narrow an `unknown` value to a
|
|
2746
|
-
* string.
|
|
2747
|
-
*
|
|
2748
|
-
* **Details**
|
|
2749
|
-
*
|
|
2750
|
-
* Uses `typeof input === "string"`.
|
|
2751
|
-
*
|
|
2752
|
-
* **Example** (Guarding strings)
|
|
2753
|
-
*
|
|
2754
|
-
* ```ts
|
|
2755
|
-
* import { Predicate } from "effect"
|
|
2756
|
-
*
|
|
2757
|
-
* const data: unknown = "hi"
|
|
2758
|
-
*
|
|
2759
|
-
* if (Predicate.isString(data)) {
|
|
2760
|
-
* data.toUpperCase() // => "HI"
|
|
2761
|
-
* }
|
|
2762
|
-
* ```
|
|
2763
|
-
*
|
|
2764
|
-
* @see {@link isNumber}
|
|
2765
|
-
* @see {@link isBoolean}
|
|
2766
|
-
* @see {@link Refinement}
|
|
2767
|
-
* @category guards
|
|
2768
|
-
* @since 2.0.0
|
|
2769
|
-
*/
|
|
2770
2091
|
function isString(input) {
|
|
2771
2092
|
return typeof input === "string";
|
|
2772
2093
|
}
|
|
2773
|
-
/**
|
|
2774
|
-
* Checks whether a value is a `function`.
|
|
2775
|
-
*
|
|
2776
|
-
* **When to use**
|
|
2777
|
-
*
|
|
2778
|
-
* Use when you need a `Predicate` guard to narrow an `unknown` value to a
|
|
2779
|
-
* callable function.
|
|
2780
|
-
*
|
|
2781
|
-
* **Details**
|
|
2782
|
-
*
|
|
2783
|
-
* Uses `typeof input === "function"`.
|
|
2784
|
-
*
|
|
2785
|
-
* **Example** (Guarding functions)
|
|
2786
|
-
*
|
|
2787
|
-
* ```ts
|
|
2788
|
-
* import { Predicate } from "effect"
|
|
2789
|
-
*
|
|
2790
|
-
* const data: unknown = () => 1
|
|
2791
|
-
*
|
|
2792
|
-
* if (Predicate.isFunction(data)) {
|
|
2793
|
-
* data() // => 1
|
|
2794
|
-
* }
|
|
2795
|
-
* ```
|
|
2796
|
-
*
|
|
2797
|
-
* @see {@link isObjectKeyword}
|
|
2798
|
-
* @category guards
|
|
2799
|
-
* @since 2.0.0
|
|
2800
|
-
*/
|
|
2801
2094
|
function isFunction(input) {
|
|
2802
2095
|
return typeof input === "function";
|
|
2803
2096
|
}
|
|
2804
|
-
/**
|
|
2805
|
-
* Checks whether a value is an `object` in the JavaScript sense (objects, arrays, functions).
|
|
2806
|
-
*
|
|
2807
|
-
* **When to use**
|
|
2808
|
-
*
|
|
2809
|
-
* Use when you need a `Predicate` guard that accepts arrays and functions as
|
|
2810
|
-
* well as objects.
|
|
2811
|
-
*
|
|
2812
|
-
* **Details**
|
|
2813
|
-
*
|
|
2814
|
-
* Returns `true` for arrays and functions, and `false` for `null`.
|
|
2815
|
-
*
|
|
2816
|
-
* **Example** (Checking object keywords)
|
|
2817
|
-
*
|
|
2818
|
-
* ```ts
|
|
2819
|
-
* import { Predicate } from "effect"
|
|
2820
|
-
*
|
|
2821
|
-
* Predicate.isObjectKeyword(() => 1) // => true
|
|
2822
|
-
* Predicate.isObjectKeyword(null) // => false
|
|
2823
|
-
* ```
|
|
2824
|
-
*
|
|
2825
|
-
* @see {@link isObject}
|
|
2826
|
-
* @see {@link isObjectOrArray}
|
|
2827
|
-
* @category guards
|
|
2828
|
-
* @since 4.0.0
|
|
2829
|
-
*/
|
|
2830
2097
|
function isObjectKeyword(input) {
|
|
2831
2098
|
return typeof input === "object" && input !== null || isFunction(input);
|
|
2832
2099
|
}
|
|
2833
|
-
/**
|
|
2834
|
-
* Checks whether a value has a given property key.
|
|
2835
|
-
*
|
|
2836
|
-
* **When to use**
|
|
2837
|
-
*
|
|
2838
|
-
* Use when you need a `Predicate` guard for property access on `unknown`
|
|
2839
|
-
* values with a simple structural object check.
|
|
2840
|
-
*
|
|
2841
|
-
* **Details**
|
|
2842
|
-
*
|
|
2843
|
-
* Uses the `in` operator and `isObjectKeyword`. This does not check property
|
|
2844
|
-
* value types.
|
|
2845
|
-
*
|
|
2846
|
-
* **Example** (Guarding object properties)
|
|
2847
|
-
*
|
|
2848
|
-
* ```ts
|
|
2849
|
-
* import { Predicate } from "effect"
|
|
2850
|
-
*
|
|
2851
|
-
* const hasName = Predicate.hasProperty("name")
|
|
2852
|
-
* const data: unknown = { name: "Ada" }
|
|
2853
|
-
*
|
|
2854
|
-
* if (hasName(data)) {
|
|
2855
|
-
* data.name // => "Ada"
|
|
2856
|
-
* }
|
|
2857
|
-
* ```
|
|
2858
|
-
*
|
|
2859
|
-
* @see {@link isTagged}
|
|
2860
|
-
* @see {@link isObjectKeyword}
|
|
2861
|
-
* @category guards
|
|
2862
|
-
* @since 2.0.0
|
|
2863
|
-
*/
|
|
2864
2100
|
const hasProperty = dual(2, (data, property) => isObjectKeyword(data) && property in data);
|
|
2865
2101
|
function getOrInsertComputed(map, key, callback) {
|
|
2866
2102
|
if (map.has(key)) return map.get(key);
|
|
@@ -2868,31 +2104,6 @@ function getOrInsertComputed(map, key, callback) {
|
|
|
2868
2104
|
map.set(key, value);
|
|
2869
2105
|
return value;
|
|
2870
2106
|
}
|
|
2871
|
-
/**
|
|
2872
|
-
* Drops elements from the start while the predicate holds, returning the rest.
|
|
2873
|
-
*
|
|
2874
|
-
* **When to use**
|
|
2875
|
-
*
|
|
2876
|
-
* Use to remove a leading prefix of elements that satisfy a predicate.
|
|
2877
|
-
*
|
|
2878
|
-
* **Details**
|
|
2879
|
-
*
|
|
2880
|
-
* The predicate receives `(element, index)`.
|
|
2881
|
-
*
|
|
2882
|
-
* **Example** (Dropping while condition holds)
|
|
2883
|
-
*
|
|
2884
|
-
* ```ts
|
|
2885
|
-
* import { Array } from "effect"
|
|
2886
|
-
*
|
|
2887
|
-
* Array.dropWhile([1, 2, 3, 4, 5], (x) => x < 4) // => [4, 5]
|
|
2888
|
-
* ```
|
|
2889
|
-
*
|
|
2890
|
-
* @see {@link takeWhile} — keep the matching prefix instead
|
|
2891
|
-
* @see {@link drop} — drop a fixed count
|
|
2892
|
-
*
|
|
2893
|
-
* @category getters
|
|
2894
|
-
* @since 2.0.0
|
|
2895
|
-
*/
|
|
2896
2107
|
const dropWhile = dual(2, (self, predicate) => {
|
|
2897
2108
|
const input = Array.isArray(self) ? self : Array.from(self);
|
|
2898
2109
|
const len = input.length;
|
|
@@ -2900,35 +2111,6 @@ const dropWhile = dual(2, (self, predicate) => {
|
|
|
2900
2111
|
while (idx < len && predicate(input[idx], idx)) idx++;
|
|
2901
2112
|
return input.slice(idx);
|
|
2902
2113
|
});
|
|
2903
|
-
/**
|
|
2904
|
-
* Takes elements from the start while the predicate holds, stopping at the
|
|
2905
|
-
* first element that fails.
|
|
2906
|
-
*
|
|
2907
|
-
* **When to use**
|
|
2908
|
-
*
|
|
2909
|
-
* Use to keep the leading elements of an iterable while each element satisfies
|
|
2910
|
-
* a predicate, returning the retained prefix as an array.
|
|
2911
|
-
*
|
|
2912
|
-
* **Details**
|
|
2913
|
-
*
|
|
2914
|
-
* Supports refinements for type narrowing. The predicate receives
|
|
2915
|
-
* `(element, index)`.
|
|
2916
|
-
*
|
|
2917
|
-
* **Example** (Taking while condition holds)
|
|
2918
|
-
*
|
|
2919
|
-
* ```ts
|
|
2920
|
-
* import { Array } from "effect"
|
|
2921
|
-
*
|
|
2922
|
-
* Array.takeWhile([1, 3, 2, 4, 1, 2], (x) => x < 4) // => [1, 3, 2]
|
|
2923
|
-
* ```
|
|
2924
|
-
*
|
|
2925
|
-
* @see {@link take} for keeping a fixed number of leading elements
|
|
2926
|
-
* @see {@link dropWhile} for removing the matching prefix and keeping the rest
|
|
2927
|
-
* @see {@link span} for splitting the matching prefix from the remaining elements
|
|
2928
|
-
*
|
|
2929
|
-
* @category getters
|
|
2930
|
-
* @since 2.0.0
|
|
2931
|
-
*/
|
|
2932
2114
|
const takeWhile = dual(2, (self, predicate) => {
|
|
2933
2115
|
const input = Array.isArray(self) ? self : Array.from(self);
|
|
2934
2116
|
const len = input.length;
|
|
@@ -3014,7 +2196,7 @@ function create$38(context) {
|
|
|
3014
2196
|
const { body } = component.body;
|
|
3015
2197
|
for (const member of body) if (core.isComponentWillMount(member)) context.report({
|
|
3016
2198
|
fix(fixer) {
|
|
3017
|
-
if (!("key"
|
|
2199
|
+
if (!hasProperty(member, "key")) return null;
|
|
3018
2200
|
return fixer.replaceText(member.key, "UNSAFE_componentWillMount");
|
|
3019
2201
|
},
|
|
3020
2202
|
messageId: "default",
|
|
@@ -3047,7 +2229,7 @@ function create$37(context) {
|
|
|
3047
2229
|
const { body } = component.body;
|
|
3048
2230
|
for (const member of body) if (core.isComponentWillReceiveProps(member)) context.report({
|
|
3049
2231
|
fix(fixer) {
|
|
3050
|
-
if (!("key"
|
|
2232
|
+
if (!hasProperty(member, "key")) return null;
|
|
3051
2233
|
return fixer.replaceText(member.key, "UNSAFE_componentWillReceiveProps");
|
|
3052
2234
|
},
|
|
3053
2235
|
messageId: "default",
|
|
@@ -3080,7 +2262,7 @@ function create$36(context) {
|
|
|
3080
2262
|
const { body } = component.body;
|
|
3081
2263
|
for (const member of body) if (core.isComponentWillUpdate(member)) context.report({
|
|
3082
2264
|
fix(fixer) {
|
|
3083
|
-
if (!("key"
|
|
2265
|
+
if (!hasProperty(member, "key")) return null;
|
|
3084
2266
|
return fixer.replaceText(member.key, "UNSAFE_componentWillUpdate");
|
|
3085
2267
|
},
|
|
3086
2268
|
messageId: "default",
|
|
@@ -3325,14 +2507,6 @@ function create$31(context) {
|
|
|
3325
2507
|
});
|
|
3326
2508
|
} };
|
|
3327
2509
|
}
|
|
3328
|
-
/**
|
|
3329
|
-
* Determine whether the given CallExpression can be safely auto-fixed by replacing
|
|
3330
|
-
* the usage of `forwardRef` with passing `ref` as a prop
|
|
3331
|
-
*
|
|
3332
|
-
* @param context The rule context object
|
|
3333
|
-
* @param node The CallExpression node to check
|
|
3334
|
-
* @returns True if the call can be auto-fixed, false otherwise
|
|
3335
|
-
*/
|
|
3336
2510
|
function couldFix(context, node) {
|
|
3337
2511
|
const { importSource } = getSettingsFromContext(context);
|
|
3338
2512
|
const initialScope = context.sourceCode.getScope(node);
|
|
@@ -3343,12 +2517,6 @@ function couldFix(context, node) {
|
|
|
3343
2517
|
default: return false;
|
|
3344
2518
|
}
|
|
3345
2519
|
}
|
|
3346
|
-
/**
|
|
3347
|
-
* Generates the fix for the `forwardRef` call
|
|
3348
|
-
* @param context The rule context
|
|
3349
|
-
* @param node The `forwardRef` call expression
|
|
3350
|
-
* @returns A fixer function that applies the changes
|
|
3351
|
-
*/
|
|
3352
2520
|
function buildFix(context, node) {
|
|
3353
2521
|
return (fixer) => {
|
|
3354
2522
|
const [componentNode] = node.arguments;
|
|
@@ -3360,14 +2528,6 @@ function buildFix(context, node) {
|
|
|
3360
2528
|
];
|
|
3361
2529
|
};
|
|
3362
2530
|
}
|
|
3363
|
-
/**
|
|
3364
|
-
* Generates fixes for the component's props and ref arguments
|
|
3365
|
-
* @param context The rule context
|
|
3366
|
-
* @param fixer The rule fixer
|
|
3367
|
-
* @param node The function component node
|
|
3368
|
-
* @param typeArguments The type arguments from the `forwardRef` call
|
|
3369
|
-
* @returns An array of fixes for the component's signature
|
|
3370
|
-
*/
|
|
3371
2531
|
function buildFixForComponentProps(context, fixer, node, typeArguments) {
|
|
3372
2532
|
const getText = (node) => context.sourceCode.getText(node);
|
|
3373
2533
|
const [arg0, arg1] = node.params;
|
|
@@ -3413,22 +2573,6 @@ function buildFixForComponentProps(context, fixer, node, typeArguments) {
|
|
|
3413
2573
|
|
|
3414
2574
|
//#endregion
|
|
3415
2575
|
//#region src/utils/create-implicit-prop-listener.ts
|
|
3416
|
-
/**
|
|
3417
|
-
* Creates a listener that detects JSX spread attributes implicitly passing a given prop
|
|
3418
|
-
* (ex: 'children', 'key', 'ref') to a component.
|
|
3419
|
-
*
|
|
3420
|
-
* For each union constituent of the spread argument's type, the prop is reported unless:
|
|
3421
|
-
* 1. The prop symbol's fully qualified name is allowed (pass-through of React internally defined props)
|
|
3422
|
-
* 2. The fully qualified name of the prop's type symbol (or its alias) is allowed (React type aliases)
|
|
3423
|
-
*
|
|
3424
|
-
* @param context The ESLint rule context
|
|
3425
|
-
* @param options The prop name, the allow-list predicates (receive lowercased fully qualified names), and the report callback
|
|
3426
|
-
* @param options.name The name of the prop to detect
|
|
3427
|
-
* @param options.isAllowedProp A predicate that receives the prop symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
3428
|
-
* @param options.isAllowedType A predicate that receives the prop's type symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
3429
|
-
* @param options.onImplicitProp A callback invoked with the spread attribute node when an implicit prop is detected
|
|
3430
|
-
* @returns A rule listener reporting on JSX spread attributes
|
|
3431
|
-
*/
|
|
3432
2576
|
function createImplicitPropListener(context, options) {
|
|
3433
2577
|
const { name, isAllowedProp, isAllowedType, onImplicitProp } = options;
|
|
3434
2578
|
const services = ESLintUtils.getParserServices(context, false);
|
|
@@ -3564,12 +2708,6 @@ function create$27(context) {
|
|
|
3564
2708
|
...compare(version, "18.0.0", "<") ? [] : ["string", "falsy string"]
|
|
3565
2709
|
];
|
|
3566
2710
|
const services = ESLintUtils.getParserServices(context, false);
|
|
3567
|
-
/**
|
|
3568
|
-
* Recursively inspects a node to find potential leaked conditional rendering
|
|
3569
|
-
* @param node The AST node to inspect
|
|
3570
|
-
* @param seen A set of visited identifier names to prevent infinite recursion.
|
|
3571
|
-
* @returns A report descriptor if a problem is found, otherwise `null`
|
|
3572
|
-
*/
|
|
3573
2711
|
function visit(node, seen = /* @__PURE__ */ new Set()) {
|
|
3574
2712
|
if (node == null) return null;
|
|
3575
2713
|
if (Check.isJSXExpressionContainer(node)) return visit(node.expression, seen);
|
|
@@ -3707,21 +2845,11 @@ function create$25(context) {
|
|
|
3707
2845
|
|
|
3708
2846
|
//#endregion
|
|
3709
2847
|
//#region src/rules/no-missing-key/lib.ts
|
|
3710
|
-
/**
|
|
3711
|
-
* Iterator-like methods whose callback's return value becomes an item in a rendered list,
|
|
3712
|
-
* mapped to the position of the callback in the call's argument list.
|
|
3713
|
-
* `from` covers `Array.from(iterable, mapFn)`.
|
|
3714
|
-
*/
|
|
3715
2848
|
const INDEX_PARAM_POSITIONS = /* @__PURE__ */ new Map([
|
|
3716
2849
|
["flatMap", 0],
|
|
3717
2850
|
["from", 1],
|
|
3718
2851
|
["map", 0]
|
|
3719
2852
|
]);
|
|
3720
|
-
/**
|
|
3721
|
-
* Gets the nested return statements in the node that are within the same function
|
|
3722
|
-
* @param node The AST node
|
|
3723
|
-
* @returns The nested return statements in the node
|
|
3724
|
-
*/
|
|
3725
2853
|
function getNestedReturnStatements$1(node) {
|
|
3726
2854
|
const statements = [];
|
|
3727
2855
|
const boundary = Check.isFunction(node) ? node : Traverse.findParent(node, Check.isFunction);
|
|
@@ -3750,11 +2878,6 @@ var no_missing_key_default = createRule({
|
|
|
3750
2878
|
create: create$24,
|
|
3751
2879
|
defaultOptions: []
|
|
3752
2880
|
});
|
|
3753
|
-
/**
|
|
3754
|
-
* Gets the mapping callback of an iterator-like call expression (e.g. `.map(cb)`, `.flatMap(cb)`, `Array.from(it, cb)`).
|
|
3755
|
-
* @param node The call expression to inspect.
|
|
3756
|
-
* @returns The callback function node, or `null` if the call is not an iterator-like call with a function callback.
|
|
3757
|
-
*/
|
|
3758
2881
|
function getIteratorCallback(node) {
|
|
3759
2882
|
const callee = Extract.unwrap(node.callee);
|
|
3760
2883
|
if (callee.type !== AST_NODE_TYPES.MemberExpression) return null;
|
|
@@ -3767,12 +2890,6 @@ function getIteratorCallback(node) {
|
|
|
3767
2890
|
}
|
|
3768
2891
|
function create$24(context) {
|
|
3769
2892
|
let childrenToArrayDepth = 0;
|
|
3770
|
-
/**
|
|
3771
|
-
* Checks an expression that is rendered as an item of a list,
|
|
3772
|
-
* recursing into the branches it may evaluate to.
|
|
3773
|
-
* @param node The expression to check.
|
|
3774
|
-
* @returns The report descriptors for the violations found.
|
|
3775
|
-
*/
|
|
3776
2893
|
function visitItemExpression(node) {
|
|
3777
2894
|
switch (node.type) {
|
|
3778
2895
|
case AST_NODE_TYPES.ChainExpression:
|
|
@@ -3794,12 +2911,6 @@ function create$24(context) {
|
|
|
3794
2911
|
default: return [];
|
|
3795
2912
|
}
|
|
3796
2913
|
}
|
|
3797
|
-
/**
|
|
3798
|
-
* Checks every value an iterator callback may produce as a list item,
|
|
3799
|
-
* whether from an expression body or from return statements in a block body.
|
|
3800
|
-
* @param node The callback function to check.
|
|
3801
|
-
* @returns The report descriptors for the violations found.
|
|
3802
|
-
*/
|
|
3803
2914
|
function visitIteratorCallback(node) {
|
|
3804
2915
|
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return visitItemExpression(node.body);
|
|
3805
2916
|
return getNestedReturnStatements$1(node.body).flatMap((stmt) => stmt.argument == null ? [] : visitItemExpression(stmt.argument));
|
|
@@ -3830,23 +2941,9 @@ function create$24(context) {
|
|
|
3830
2941
|
|
|
3831
2942
|
//#endregion
|
|
3832
2943
|
//#region src/rules/no-misused-capture-owner-stack/lib.ts
|
|
3833
|
-
/**
|
|
3834
|
-
* Check if the given node is a member expression that accesses `process.env.NODE_ENV`
|
|
3835
|
-
* @param context The rule context
|
|
3836
|
-
* @param node The AST node
|
|
3837
|
-
* @returns True if the node is a member expression that accesses `process.env.NODE_ENV`, false otherwise
|
|
3838
|
-
*/
|
|
3839
2944
|
function isProcessEnvNodeEnv(context, node) {
|
|
3840
2945
|
return node != null && core.isAPI("process.env.NODE_ENV")(context, node);
|
|
3841
2946
|
}
|
|
3842
|
-
/**
|
|
3843
|
-
* Check if the given node is a binary expression that compares `process.env.NODE_ENV` with a string literal.
|
|
3844
|
-
* @param context The rule context
|
|
3845
|
-
* @param node The AST node
|
|
3846
|
-
* @param operator The operator used in the comparison
|
|
3847
|
-
* @param value The string literal value to compare against
|
|
3848
|
-
* @returns True if the node is a binary expression that compares `process.env.NODE_ENV` with the specified value, false otherwise
|
|
3849
|
-
*/
|
|
3850
2947
|
function isProcessEnvNodeEnvCompare(context, node, operator, value) {
|
|
3851
2948
|
if (node == null) return false;
|
|
3852
2949
|
if (node.type !== AST_NODE_TYPES.BinaryExpression) return false;
|
|
@@ -3908,44 +3005,21 @@ function create$23(context) {
|
|
|
3908
3005
|
|
|
3909
3006
|
//#endregion
|
|
3910
3007
|
//#region src/rules/no-nested-component-definitions/lib.ts
|
|
3911
|
-
/** Check if the node is a call expression to react-redux's `connect`. */
|
|
3912
3008
|
const isConnectCall = core.isAPICall("connect");
|
|
3913
|
-
/** Check if the node is a call expression to Relay's `createFragmentContainer`. */
|
|
3914
3009
|
const isCreateFragmentContainerCall = core.isAPICall("createFragmentContainer");
|
|
3915
|
-
/** Check if the node is a call expression to Relay's `createPaginationContainer`. */
|
|
3916
3010
|
const isCreatePaginationContainerCall = core.isAPICall("createPaginationContainer");
|
|
3917
|
-
/** Check if the node is a call expression to Relay's `createRefetchContainer`. */
|
|
3918
3011
|
const isCreateRefetchContainerCall = core.isAPICall("createRefetchContainer");
|
|
3919
|
-
/** Check if the node is a call expression to React's `forwardRef`. */
|
|
3920
3012
|
const isForwardRefCall = core.isAPICall("forwardRef");
|
|
3921
|
-
/** Check if the node is a call expression to a `graphql` tag function (e.g. from Relay or Apollo). */
|
|
3922
3013
|
const isGraphqlCall = core.isAPICall("graphql");
|
|
3923
|
-
/** Check if the node is a call expression to React's `memo`. */
|
|
3924
3014
|
const isMemoCall = core.isAPICall("memo");
|
|
3925
|
-
/** Check if the node is a call expression to MobX's `observer`. */
|
|
3926
3015
|
const isObserverCall = core.isAPICall("observer");
|
|
3927
|
-
/** Check if the node is a call expression to React's `useCallback`. */
|
|
3928
3016
|
const isUseCallbackCall = core.isAPICall("useCallback");
|
|
3929
|
-
/** Check if the node is a call expression to Formik's `withFormik`. */
|
|
3930
3017
|
const isWithFormikCall = core.isAPICall("withFormik");
|
|
3931
|
-
/** Check if the node is a call expression to recompose's `withHandlers`. */
|
|
3932
3018
|
const isWithHandlersCall = core.isAPICall("withHandlers");
|
|
3933
|
-
/** Check if the node is a call expression to recompose's `withLifecycle`. */
|
|
3934
3019
|
const isWithLifecycleCall = core.isAPICall("withLifecycle");
|
|
3935
|
-
/** Check if the node is a call expression to recompose's `withProps`. */
|
|
3936
3020
|
const isWithPropsCall = core.isAPICall("withProps");
|
|
3937
|
-
/** Check if the node is a call expression to react-router v5's `withRouter`. */
|
|
3938
3021
|
const isWithRouterCall = core.isAPICall("withRouter");
|
|
3939
|
-
/** Check if the node is a call expression to recompose's `withState`. */
|
|
3940
3022
|
const isWithStateCall = core.isAPICall("withState");
|
|
3941
|
-
/**
|
|
3942
|
-
* Check if a call is a well-known component wrapper call (e.g. `memo`, `connect`, `withFormik`).
|
|
3943
|
-
* Curried forms like `connect(...)(Component)` are matched via the innermost callee call.
|
|
3944
|
-
* @param context The rule context
|
|
3945
|
-
* @param call The call expression to check
|
|
3946
|
-
* @param arg The function node passed to the call
|
|
3947
|
-
* @returns `true` if the call is a component wrapper call
|
|
3948
|
-
*/
|
|
3949
3023
|
function isComponentWrapperCall(context, call, arg) {
|
|
3950
3024
|
if (Extract.unwrap(call.callee) === arg) return false;
|
|
3951
3025
|
call = getInnermostCall(call);
|
|
@@ -3966,12 +3040,6 @@ function isComponentWrapperCall(context, call, arg) {
|
|
|
3966
3040
|
if (isWithStateCall(context, call)) return true;
|
|
3967
3041
|
return false;
|
|
3968
3042
|
}
|
|
3969
|
-
/**
|
|
3970
|
-
* Unwrap curried call wrappers like `connect(...)(Component)` to get the innermost call expression.
|
|
3971
|
-
* Type expressions and chain expressions around each callee are unwrapped along the way.
|
|
3972
|
-
* @param node The outermost call expression to inspect.
|
|
3973
|
-
* @returns The innermost call expression, whose callee is not itself a call expression.
|
|
3974
|
-
*/
|
|
3975
3043
|
function getInnermostCall(node) {
|
|
3976
3044
|
let call = node;
|
|
3977
3045
|
let callee = Extract.unwrap(call.callee);
|
|
@@ -3981,12 +3049,6 @@ function getInnermostCall(node) {
|
|
|
3981
3049
|
}
|
|
3982
3050
|
return call;
|
|
3983
3051
|
}
|
|
3984
|
-
/**
|
|
3985
|
-
* Resolve the name a function is bound to through wrapping calls (e.g. `const C = useCallback(...)` → `C`).
|
|
3986
|
-
* @param context The rule context
|
|
3987
|
-
* @param node The function node to resolve the bound name for
|
|
3988
|
-
* @returns The bound name, or `null` if the call chain does not end at an identifier declarator
|
|
3989
|
-
*/
|
|
3990
3052
|
function getWrapperCallBoundName(context, node) {
|
|
3991
3053
|
let current = node;
|
|
3992
3054
|
let parent = current.parent;
|
|
@@ -4005,20 +3067,9 @@ function getWrapperCallBoundName(context, node) {
|
|
|
4005
3067
|
if (parent.type !== AST_NODE_TYPES.VariableDeclarator || !Check.isIdentifier(parent.id)) return null;
|
|
4006
3068
|
return parent.id.name;
|
|
4007
3069
|
}
|
|
4008
|
-
/**
|
|
4009
|
-
* Check if the node is inside a JSX attribute value
|
|
4010
|
-
* @param node The AST node to check
|
|
4011
|
-
* @returns `true` if the node is inside a JSX attribute value
|
|
4012
|
-
*/
|
|
4013
3070
|
function isInsideJSXAttributeValue(node) {
|
|
4014
3071
|
return node.parent.type === AST_NODE_TYPES.JSXAttribute || findParentAttribute(node, (n) => n.value?.type === AST_NODE_TYPES.JSXExpressionContainer) != null;
|
|
4015
3072
|
}
|
|
4016
|
-
/**
|
|
4017
|
-
* Check if the node is declared inside a class component's render block
|
|
4018
|
-
* Ex: class C extends React.Component { render() { const Nested = () => <div />; } }
|
|
4019
|
-
* @param node The AST node to check
|
|
4020
|
-
* @returns `true` if the node is inside a class component's render block
|
|
4021
|
-
*/
|
|
4022
3073
|
function isInsideRenderMethod(node) {
|
|
4023
3074
|
return Traverse.findParent(node, (n) => core.isRenderMethodLike(n) && core.isClassComponent(n.parent.parent)) != null;
|
|
4024
3075
|
}
|
|
@@ -4386,7 +3437,7 @@ function isContextName(name, isReact18OrBelow) {
|
|
|
4386
3437
|
}
|
|
4387
3438
|
function getHumanReadableKind$1(node) {
|
|
4388
3439
|
if (node.type === AST_NODE_TYPES.Literal) {
|
|
4389
|
-
if ("regex"
|
|
3440
|
+
if (hasProperty(node, "regex")) return "regexp literal";
|
|
4390
3441
|
if (node.value === null) return "null literal";
|
|
4391
3442
|
return `${typeof node.value} literal`;
|
|
4392
3443
|
}
|
|
@@ -4423,7 +3474,7 @@ function create$13(context) {
|
|
|
4423
3474
|
if (enclosingFunction == null) return;
|
|
4424
3475
|
if (compilationMode === "annotation" && core.isFunctionHasDirective(enclosingFunction, "use memo")) return;
|
|
4425
3476
|
const attribute = node.attributes.find((attribute) => attribute.type === AST_NODE_TYPES.JSXAttribute && attribute.name.name === "value");
|
|
4426
|
-
if (attribute == null || !("value"
|
|
3477
|
+
if (attribute == null || !hasProperty(attribute, "value")) return;
|
|
4427
3478
|
const value = attribute.value;
|
|
4428
3479
|
if (value?.type !== AST_NODE_TYPES.JSXExpressionContainer) return;
|
|
4429
3480
|
const valueExpression = value.expression;
|
|
@@ -4461,7 +3512,7 @@ const SEL_OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR = [
|
|
|
4461
3512
|
].join("");
|
|
4462
3513
|
function getHumanReadableKind(node) {
|
|
4463
3514
|
if (node.type === AST_NODE_TYPES.Literal) {
|
|
4464
|
-
if ("regex"
|
|
3515
|
+
if (hasProperty(node, "regex")) return "regexp literal";
|
|
4465
3516
|
if (node.value === null) return "null literal";
|
|
4466
3517
|
return `${typeof node.value} literal`;
|
|
4467
3518
|
}
|
|
@@ -4848,9 +3899,6 @@ function create$8(context) {
|
|
|
4848
3899
|
|
|
4849
3900
|
//#endregion
|
|
4850
3901
|
//#region src/rules/purity/lib.ts
|
|
4851
|
-
/**
|
|
4852
|
-
* Known impure functions
|
|
4853
|
-
*/
|
|
4854
3902
|
const IMPURE_FUNCS = /* @__PURE__ */ new Map([
|
|
4855
3903
|
["Atomics", /* @__PURE__ */ new Set([
|
|
4856
3904
|
"add",
|
|
@@ -5101,9 +4149,6 @@ const IMPURE_FUNCS = /* @__PURE__ */ new Map([
|
|
|
5101
4149
|
"structuredClone"
|
|
5102
4150
|
])]
|
|
5103
4151
|
]);
|
|
5104
|
-
/**
|
|
5105
|
-
* Known impure global constructors used with `new`
|
|
5106
|
-
*/
|
|
5107
4152
|
const IMPURE_CTORS = /* @__PURE__ */ new Set([
|
|
5108
4153
|
"Audio",
|
|
5109
4154
|
"AudioContext",
|
|
@@ -5129,9 +4174,6 @@ const IMPURE_CTORS = /* @__PURE__ */ new Set([
|
|
|
5129
4174
|
"Worker",
|
|
5130
4175
|
"XMLHttpRequest"
|
|
5131
4176
|
]);
|
|
5132
|
-
/**
|
|
5133
|
-
* Known pure functions
|
|
5134
|
-
*/
|
|
5135
4177
|
const PURE_FUNCS = /* @__PURE__ */ new Map([
|
|
5136
4178
|
["Array", /* @__PURE__ */ new Set([
|
|
5137
4179
|
"from",
|
|
@@ -5239,18 +4281,9 @@ const PURE_FUNCS = /* @__PURE__ */ new Map([
|
|
|
5239
4281
|
function isCatalogObject(name) {
|
|
5240
4282
|
return IMPURE_FUNCS.has(name) || PURE_FUNCS.has(name);
|
|
5241
4283
|
}
|
|
5242
|
-
/**
|
|
5243
|
-
* Check if the program has a top-level `use client` directive, marking the
|
|
5244
|
-
* module as a client module in React Server Components conventions.
|
|
5245
|
-
*/
|
|
5246
4284
|
function hasUseClientDirective(program) {
|
|
5247
4285
|
return program.body.some((stmt) => stmt.type === AST_NODE_TYPES.ExpressionStatement && stmt.directive === "use client");
|
|
5248
4286
|
}
|
|
5249
|
-
/**
|
|
5250
|
-
* Find the property key under which `name` is bound in an object destructuring
|
|
5251
|
-
* pattern, e.g. `"random"` for `random` in `const { random } = Math` or for
|
|
5252
|
-
* `r` in `const { random: r } = Math`.
|
|
5253
|
-
*/
|
|
5254
4287
|
function getDestructuredPropertyName(pattern, name) {
|
|
5255
4288
|
for (const property of pattern.properties) {
|
|
5256
4289
|
if (property.type !== AST_NODE_TYPES.Property) continue;
|
|
@@ -5259,13 +4292,6 @@ function getDestructuredPropertyName(pattern, name) {
|
|
|
5259
4292
|
}
|
|
5260
4293
|
return null;
|
|
5261
4294
|
}
|
|
5262
|
-
/**
|
|
5263
|
-
* Resolve a member expression to a builtin object name and property, e.g.
|
|
5264
|
-
* `window.Math.random` resolves to `{ object: "Math", property: "random" }`.
|
|
5265
|
-
* Intermediate segments are followed only while they name known catalog
|
|
5266
|
-
* objects (`window`, `Math`, ...), so unknown-global chains like
|
|
5267
|
-
* `foo.Math.random` do not resolve.
|
|
5268
|
-
*/
|
|
5269
4295
|
function resolveBuiltinMember(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
5270
4296
|
const chain = Extract.getMemberChain(node);
|
|
5271
4297
|
const rootId = chain.at(0);
|
|
@@ -5285,17 +4311,6 @@ function resolveBuiltinMember(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
|
5285
4311
|
property: names.at(-1) ?? null
|
|
5286
4312
|
};
|
|
5287
4313
|
}
|
|
5288
|
-
/**
|
|
5289
|
-
* Recursively resolve an identifier to a builtin global object name and
|
|
5290
|
-
* optional property. Follows simple assignment chains like `const M = Math`,
|
|
5291
|
-
* member-function aliases like `const random = Math.random`, and destructured
|
|
5292
|
-
* aliases like `const { random } = Math`.
|
|
5293
|
-
* Returns `null` if the identifier is locally defined (parameter, import, function declaration, etc.)
|
|
5294
|
-
* or resolves to a non-builtin source.
|
|
5295
|
-
* @param context - The rule context.
|
|
5296
|
-
* @param node - The identifier node to resolve.
|
|
5297
|
-
* @param seen - A set of already visited identifier names to prevent infinite loops.
|
|
5298
|
-
*/
|
|
5299
4314
|
function resolveBuiltinObjectName(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
5300
4315
|
if (seen.has(node.name)) return null;
|
|
5301
4316
|
seen.add(node.name);
|
|
@@ -5467,13 +4482,6 @@ function getRefAccess(node) {
|
|
|
5467
4482
|
node
|
|
5468
4483
|
};
|
|
5469
4484
|
}
|
|
5470
|
-
/**
|
|
5471
|
-
* Check whether `node` (a `ref.current` MemberExpression) is assigned through a
|
|
5472
|
-
* destructuring pattern or a for-in/of loop target, e.g.
|
|
5473
|
-
* `({ a: ref.current } = value)`, `[ref.current] = value`,
|
|
5474
|
-
* `({ a: ref.current.x } = value)`, or `for (ref.current of items)`. Defaults
|
|
5475
|
-
* and computed keys inside patterns are reads, not writes.
|
|
5476
|
-
*/
|
|
5477
4485
|
function isPatternWriteTarget(node) {
|
|
5478
4486
|
let child = node;
|
|
5479
4487
|
let current = node.parent;
|
|
@@ -5500,10 +4508,6 @@ function isPatternWriteTarget(node) {
|
|
|
5500
4508
|
current = current.parent;
|
|
5501
4509
|
}
|
|
5502
4510
|
}
|
|
5503
|
-
/**
|
|
5504
|
-
* Check whether `node` (a `ref.current` MemberExpression) is being written to indirectly through
|
|
5505
|
-
* a nested property write, e.g. `ref.current.inner = value` or `ref.current.inner++`.
|
|
5506
|
-
*/
|
|
5507
4511
|
function isNestedRefCurrentWrite(node) {
|
|
5508
4512
|
let outer = node;
|
|
5509
4513
|
let sawNesting = false;
|
|
@@ -5522,11 +4526,6 @@ function isNestedRefCurrentWrite(node) {
|
|
|
5522
4526
|
return false;
|
|
5523
4527
|
}
|
|
5524
4528
|
}
|
|
5525
|
-
/**
|
|
5526
|
-
* Parse an exact nullish check and return the branch where the checked value is null/undefined.
|
|
5527
|
-
* Truthiness checks such as `!ref.current` are deliberately excluded: falsy ref values are not
|
|
5528
|
-
* necessarily uninitialized.
|
|
5529
|
-
*/
|
|
5530
4529
|
function getNullCheckBranch(test, isCheckedValue, isNullishValue) {
|
|
5531
4530
|
const unwrapped = Extract.unwrap(test);
|
|
5532
4531
|
if (unwrapped.type === AST_NODE_TYPES.UnaryExpression && unwrapped.operator === "!") {
|
|
@@ -5565,7 +4564,6 @@ function getGuardDisposition(node, getNullBranch) {
|
|
|
5565
4564
|
}
|
|
5566
4565
|
return null;
|
|
5567
4566
|
}
|
|
5568
|
-
/** Return whether every path through a statement exits the current function. */
|
|
5569
4567
|
function isUnconditionallyTerminating(statement) {
|
|
5570
4568
|
switch (statement.type) {
|
|
5571
4569
|
case AST_NODE_TYPES.ReturnStatement:
|
|
@@ -5599,14 +4597,6 @@ function isAfterTerminatingNonNullGuard(node, getNullBranch) {
|
|
|
5599
4597
|
|
|
5600
4598
|
//#endregion
|
|
5601
4599
|
//#region src/rules/refs/collect.ts
|
|
5602
|
-
/**
|
|
5603
|
-
* Collect the raw facts the refs rule reasons about: identifier/member/function
|
|
5604
|
-
* bindings with their source positions, JSX `ref` attribute targets, call
|
|
5605
|
-
* expressions, ref pass sites (calls, constructor calls, tagged templates), and
|
|
5606
|
-
* every `.current` member access. Facts outside any function are never
|
|
5607
|
-
* reachable during render and are not collected. All inference is deferred to
|
|
5608
|
-
* `Program:exit`.
|
|
5609
|
-
*/
|
|
5610
4600
|
function createFactCollector$4() {
|
|
5611
4601
|
const facts = {
|
|
5612
4602
|
bindings: [],
|
|
@@ -5706,11 +4696,9 @@ function createFactCollector$4() {
|
|
|
5706
4696
|
|
|
5707
4697
|
//#endregion
|
|
5708
4698
|
//#region src/rules/refs/effects.ts
|
|
5709
|
-
/** The nearest strict ancestor of `node` that is a component or hook. */
|
|
5710
4699
|
function findBoundary(node, boundaries) {
|
|
5711
4700
|
return Traverse.findParent(node, (candidate) => Check.isFunction(candidate) && boundaries.has(candidate));
|
|
5712
4701
|
}
|
|
5713
|
-
/** The boundary containing `node`, where `node` is the nearest enclosing function of the fact. */
|
|
5714
4702
|
function getBoundaryOf(node, boundaries) {
|
|
5715
4703
|
return boundaries.has(node) ? node : findBoundary(node, boundaries);
|
|
5716
4704
|
}
|
|
@@ -5718,12 +4706,6 @@ function isReachedDuringRender(node, boundary, reachability) {
|
|
|
5718
4706
|
const reached = reachability.get(boundary);
|
|
5719
4707
|
return reached != null && isReachedThroughFunctions(node, boundary, reached);
|
|
5720
4708
|
}
|
|
5721
|
-
/**
|
|
5722
|
-
* Resolve the collected call edges into a per-boundary render call graph.
|
|
5723
|
-
* Direct calls, IIFEs, known synchronous callbacks, function declarations,
|
|
5724
|
-
* aliases, and simple object-property bindings all share the same
|
|
5725
|
-
* identity-based resolver. Each call target is resolved exactly once.
|
|
5726
|
-
*/
|
|
5727
4709
|
function inferCallGraph(boundaries, callEdges, resolveCallable) {
|
|
5728
4710
|
const callGraph = /* @__PURE__ */ new Map();
|
|
5729
4711
|
for (const edge of callEdges) {
|
|
@@ -5746,10 +4728,6 @@ function inferCallGraph(boundaries, callEdges, resolveCallable) {
|
|
|
5746
4728
|
}
|
|
5747
4729
|
return callGraph;
|
|
5748
4730
|
}
|
|
5749
|
-
/**
|
|
5750
|
-
* Walk the call graph from each render boundary and gather every function
|
|
5751
|
-
* reached during that boundary's render.
|
|
5752
|
-
*/
|
|
5753
4731
|
function collectReachableFunctions(boundaries, callGraph) {
|
|
5754
4732
|
const reachedByBoundary = /* @__PURE__ */ new Map();
|
|
5755
4733
|
for (const boundary of boundaries) {
|
|
@@ -5766,10 +4744,6 @@ function collectReachableFunctions(boundaries, callGraph) {
|
|
|
5766
4744
|
}
|
|
5767
4745
|
return reachedByBoundary;
|
|
5768
4746
|
}
|
|
5769
|
-
/**
|
|
5770
|
-
* Validate the reached `.current` accesses and track the one permitted lazy
|
|
5771
|
-
* initialization per ref.
|
|
5772
|
-
*/
|
|
5773
4747
|
function inferRefViolations(accesses, boundaries, reachability, resolver) {
|
|
5774
4748
|
const violations = [];
|
|
5775
4749
|
const initializedRefs = /* @__PURE__ */ new Map();
|
|
@@ -5806,31 +4780,18 @@ function inferRefViolations(accesses, boundaries, reachability, resolver) {
|
|
|
5806
4780
|
}
|
|
5807
4781
|
return violations;
|
|
5808
4782
|
}
|
|
5809
|
-
/**
|
|
5810
|
-
* Get the statically known name of a pass-site callee, whether it is a plain
|
|
5811
|
-
* call, a constructor call, or a tagged template.
|
|
5812
|
-
*/
|
|
5813
4783
|
function getPassSiteCalleeName(node) {
|
|
5814
4784
|
const callee = Extract.unwrap(node.type === AST_NODE_TYPES.TaggedTemplateExpression ? node.tag : node.callee);
|
|
5815
4785
|
if (Check.isIdentifier(callee)) return callee.name;
|
|
5816
4786
|
if (callee.type === AST_NODE_TYPES.MemberExpression && !callee.computed && Check.isIdentifier(callee.property)) return callee.property.name;
|
|
5817
4787
|
return null;
|
|
5818
4788
|
}
|
|
5819
|
-
/**
|
|
5820
|
-
* The `mergeRefs` exemption is name-based but alias-aware: a simple variable
|
|
5821
|
-
* alias of a `mergeRefs` binding keeps the exemption.
|
|
5822
|
-
*/
|
|
5823
4789
|
function isMergeRefsCallee(callee, calleeName, position, resolver) {
|
|
5824
4790
|
if (calleeName === "mergeRefs") return true;
|
|
5825
4791
|
if (callee.type !== AST_NODE_TYPES.Identifier) return false;
|
|
5826
4792
|
const variable = resolver.getVariable(callee);
|
|
5827
4793
|
return variable != null && resolver.resolveAliasTarget(variable, position).name === "mergeRefs";
|
|
5828
4794
|
}
|
|
5829
|
-
/**
|
|
5830
|
-
* Passing a ref to an unknown function can expose its value during render.
|
|
5831
|
-
* Hook callbacks, mergeRefs, and the existing render-prop compatibility case
|
|
5832
|
-
* remain exempt.
|
|
5833
|
-
*/
|
|
5834
4795
|
function inferRefPassViolations(passSites, boundaries, reachability, resolver) {
|
|
5835
4796
|
const violations = [];
|
|
5836
4797
|
for (const site of passSites) {
|
|
@@ -5860,11 +4821,6 @@ function inferRefPassViolations(passSites, boundaries, reachability, resolver) {
|
|
|
5860
4821
|
|
|
5861
4822
|
//#endregion
|
|
5862
4823
|
//#region src/rules/refs/origins.ts
|
|
5863
|
-
/**
|
|
5864
|
-
* Resolve which variables hold refs and which resolve to callable functions.
|
|
5865
|
-
* Maps are keyed by ESLint variables, not source names, so shadowed
|
|
5866
|
-
* identifiers and separate components cannot contaminate each other.
|
|
5867
|
-
*/
|
|
5868
4824
|
function createBindingResolver(context, facts) {
|
|
5869
4825
|
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
5870
4826
|
const bindings = /* @__PURE__ */ new Map();
|
|
@@ -5957,11 +4913,6 @@ function createBindingResolver(context, facts) {
|
|
|
5957
4913
|
case "unknown": return null;
|
|
5958
4914
|
}
|
|
5959
4915
|
}
|
|
5960
|
-
/**
|
|
5961
|
-
* Follow simple variable-to-variable aliases to the terminal variable, e.g.
|
|
5962
|
-
* `const combine = mergeRefs` resolves to the `mergeRefs` variable itself.
|
|
5963
|
-
* Position-aware, so a reassigned alias resolves to its latest source.
|
|
5964
|
-
*/
|
|
5965
4916
|
function resolveAliasTarget(variable, position, seen = /* @__PURE__ */ new Set()) {
|
|
5966
4917
|
if (seen.has(variable)) return variable;
|
|
5967
4918
|
seen.add(variable);
|
|
@@ -6073,38 +5024,17 @@ function create$6(context) {
|
|
|
6073
5024
|
function assert(cond, message) {
|
|
6074
5025
|
if (!cond) throw new Error(message ?? "Assertion violated.");
|
|
6075
5026
|
}
|
|
6076
|
-
/**
|
|
6077
|
-
* Checks whether or not a given segment is reachable.
|
|
6078
|
-
*/
|
|
6079
5027
|
function isReachable$1(segment) {
|
|
6080
5028
|
return segment.reachable;
|
|
6081
5029
|
}
|
|
6082
|
-
/**
|
|
6083
|
-
* A code path segment.
|
|
6084
|
-
*/
|
|
6085
5030
|
var CodePathSegment = class CodePathSegment {
|
|
6086
|
-
/**
|
|
6087
|
-
* The identifier of this code path.
|
|
6088
|
-
* Rules use it to store additional information of each rule.
|
|
6089
|
-
*/
|
|
6090
5031
|
id;
|
|
6091
|
-
/** An array of the next reachable segments. */
|
|
6092
5032
|
nextSegments;
|
|
6093
|
-
/** An array of the previous reachable segments. */
|
|
6094
5033
|
prevSegments;
|
|
6095
|
-
/** An array of all next segments (including unreachable). */
|
|
6096
5034
|
allNextSegments;
|
|
6097
|
-
/** An array of all previous segments (including unreachable). */
|
|
6098
5035
|
allPrevSegments;
|
|
6099
|
-
/** Whether this segment is reachable. */
|
|
6100
5036
|
reachable;
|
|
6101
|
-
/** Internal bookkeeping data. */
|
|
6102
5037
|
internal;
|
|
6103
|
-
/**
|
|
6104
|
-
* @param id An identifier.
|
|
6105
|
-
* @param allPrevSegments An array of the previous segments (including unreachable).
|
|
6106
|
-
* @param reachable Whether this segment is reachable.
|
|
6107
|
-
*/
|
|
6108
5038
|
constructor(id, allPrevSegments, reachable) {
|
|
6109
5039
|
this.id = id;
|
|
6110
5040
|
this.nextSegments = [];
|
|
@@ -6117,44 +5047,23 @@ var CodePathSegment = class CodePathSegment {
|
|
|
6117
5047
|
loopedPrevSegments: []
|
|
6118
5048
|
};
|
|
6119
5049
|
}
|
|
6120
|
-
/**
|
|
6121
|
-
* Checks if a given previous segment is coming from the end of a loop.
|
|
6122
|
-
*/
|
|
6123
5050
|
isLoopedPrevSegment(segment) {
|
|
6124
5051
|
return this.internal.loopedPrevSegments.includes(segment);
|
|
6125
5052
|
}
|
|
6126
|
-
/**
|
|
6127
|
-
* Creates the root segment.
|
|
6128
|
-
*/
|
|
6129
5053
|
static newRoot(id) {
|
|
6130
5054
|
return new CodePathSegment(id, [], true);
|
|
6131
5055
|
}
|
|
6132
|
-
/**
|
|
6133
|
-
* Creates a segment that follows given segments.
|
|
6134
|
-
*/
|
|
6135
5056
|
static newNext(id, allPrevSegments) {
|
|
6136
5057
|
return new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), allPrevSegments.some(isReachable$1));
|
|
6137
5058
|
}
|
|
6138
|
-
/**
|
|
6139
|
-
* Creates an unreachable segment that follows given segments.
|
|
6140
|
-
*/
|
|
6141
5059
|
static newUnreachable(id, allPrevSegments) {
|
|
6142
5060
|
const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
|
|
6143
5061
|
CodePathSegment.markUsed(segment);
|
|
6144
5062
|
return segment;
|
|
6145
5063
|
}
|
|
6146
|
-
/**
|
|
6147
|
-
* Creates a segment that follows given segments.
|
|
6148
|
-
* This factory method does not connect with `allPrevSegments`.
|
|
6149
|
-
* But this inherits `reachable` flag.
|
|
6150
|
-
*/
|
|
6151
5064
|
static newDisconnected(id, allPrevSegments) {
|
|
6152
5065
|
return new CodePathSegment(id, [], allPrevSegments.some(isReachable$1));
|
|
6153
5066
|
}
|
|
6154
|
-
/**
|
|
6155
|
-
* Makes a given segment being used.
|
|
6156
|
-
* Also registers the segment into the previous segments as a next.
|
|
6157
|
-
*/
|
|
6158
5067
|
static markUsed(segment) {
|
|
6159
5068
|
if (segment.internal.used) return;
|
|
6160
5069
|
segment.internal.used = true;
|
|
@@ -6166,15 +5075,9 @@ var CodePathSegment = class CodePathSegment {
|
|
|
6166
5075
|
}
|
|
6167
5076
|
else for (i = 0; i < segment.allPrevSegments.length; ++i) segment.allPrevSegments[i].allNextSegments.push(segment);
|
|
6168
5077
|
}
|
|
6169
|
-
/**
|
|
6170
|
-
* Marks a previous segment as looped.
|
|
6171
|
-
*/
|
|
6172
5078
|
static markPrevSegmentAsLooped(segment, prevSegment) {
|
|
6173
5079
|
segment.internal.loopedPrevSegments.push(prevSegment);
|
|
6174
5080
|
}
|
|
6175
|
-
/**
|
|
6176
|
-
* Replaces unused segments with the previous segments of each unused segment.
|
|
6177
|
-
*/
|
|
6178
5081
|
static flattenUnusedSegments(segments) {
|
|
6179
5082
|
const done = Object.create(null);
|
|
6180
5083
|
const retv = [];
|
|
@@ -6196,19 +5099,9 @@ var CodePathSegment = class CodePathSegment {
|
|
|
6196
5099
|
return retv;
|
|
6197
5100
|
}
|
|
6198
5101
|
};
|
|
6199
|
-
/**
|
|
6200
|
-
* Gets whether or not a given segment is reachable.
|
|
6201
|
-
*/
|
|
6202
5102
|
function isReachable(segment) {
|
|
6203
5103
|
return segment.reachable;
|
|
6204
5104
|
}
|
|
6205
|
-
/**
|
|
6206
|
-
* Creates new segments from the specific range of `context.segmentsList`.
|
|
6207
|
-
*
|
|
6208
|
-
* When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
|
|
6209
|
-
* `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
|
|
6210
|
-
* This `h` is from `b`, `d`, and `f`.
|
|
6211
|
-
*/
|
|
6212
5105
|
function makeSegments(context, begin, end, create) {
|
|
6213
5106
|
const list = context.segmentsList;
|
|
6214
5107
|
const normalizedBegin = begin >= 0 ? begin : list.length + begin;
|
|
@@ -6221,12 +5114,6 @@ function makeSegments(context, begin, end, create) {
|
|
|
6221
5114
|
}
|
|
6222
5115
|
return segments;
|
|
6223
5116
|
}
|
|
6224
|
-
/**
|
|
6225
|
-
* `segments` becomes doubly in a `finally` block. Then if a code path exits by a
|
|
6226
|
-
* control statement (such as `break`, `continue`) from the `finally` block, the
|
|
6227
|
-
* destination's segments may be half of the source segments. In that case, this
|
|
6228
|
-
* merges segments.
|
|
6229
|
-
*/
|
|
6230
5117
|
function mergeExtraSegments(context, segments) {
|
|
6231
5118
|
let currentSegments = segments;
|
|
6232
5119
|
while (currentSegments.length > context.count) {
|
|
@@ -6236,9 +5123,6 @@ function mergeExtraSegments(context, segments) {
|
|
|
6236
5123
|
}
|
|
6237
5124
|
return currentSegments;
|
|
6238
5125
|
}
|
|
6239
|
-
/**
|
|
6240
|
-
* A class to manage forking.
|
|
6241
|
-
*/
|
|
6242
5126
|
var ForkContext = class ForkContext {
|
|
6243
5127
|
idGenerator;
|
|
6244
5128
|
upper;
|
|
@@ -6250,91 +5134,51 @@ var ForkContext = class ForkContext {
|
|
|
6250
5134
|
this.count = count;
|
|
6251
5135
|
this.segmentsList = [];
|
|
6252
5136
|
}
|
|
6253
|
-
/** The head segments. */
|
|
6254
5137
|
get head() {
|
|
6255
5138
|
const list = this.segmentsList;
|
|
6256
5139
|
return list.length === 0 ? [] : list[list.length - 1];
|
|
6257
5140
|
}
|
|
6258
|
-
/** Whether the segments list is empty. */
|
|
6259
5141
|
get empty() {
|
|
6260
5142
|
return this.segmentsList.length === 0;
|
|
6261
5143
|
}
|
|
6262
|
-
/** Whether any head segment is reachable. */
|
|
6263
5144
|
get reachable() {
|
|
6264
5145
|
const segments = this.head;
|
|
6265
5146
|
return segments.length > 0 && segments.some(isReachable);
|
|
6266
5147
|
}
|
|
6267
|
-
/**
|
|
6268
|
-
* Creates new segments from this context.
|
|
6269
|
-
*/
|
|
6270
5148
|
makeNext(begin, end) {
|
|
6271
5149
|
return makeSegments(this, begin, end, CodePathSegment.newNext);
|
|
6272
5150
|
}
|
|
6273
|
-
/**
|
|
6274
|
-
* Creates new unreachable segments from this context.
|
|
6275
|
-
*/
|
|
6276
5151
|
makeUnreachable(begin, end) {
|
|
6277
5152
|
return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
|
|
6278
5153
|
}
|
|
6279
|
-
/**
|
|
6280
|
-
* Creates new disconnected segments from this context.
|
|
6281
|
-
* The new segments don't have connections for previous segments,
|
|
6282
|
-
* but inherit the reachable flag.
|
|
6283
|
-
*/
|
|
6284
5154
|
makeDisconnected(begin, end) {
|
|
6285
5155
|
return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
|
|
6286
5156
|
}
|
|
6287
|
-
/**
|
|
6288
|
-
* Adds segments into this context. The added segments become the head.
|
|
6289
|
-
*/
|
|
6290
5157
|
add(segments) {
|
|
6291
5158
|
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
|
|
6292
5159
|
this.segmentsList.push(mergeExtraSegments(this, segments));
|
|
6293
5160
|
}
|
|
6294
|
-
/**
|
|
6295
|
-
* Replaces the head segments with given segments.
|
|
6296
|
-
* The current head segments are removed.
|
|
6297
|
-
*/
|
|
6298
5161
|
replaceHead(segments) {
|
|
6299
5162
|
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
|
|
6300
5163
|
this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
|
|
6301
5164
|
}
|
|
6302
|
-
/**
|
|
6303
|
-
* Adds all segments of a given fork context into this context.
|
|
6304
|
-
*/
|
|
6305
5165
|
addAll(context) {
|
|
6306
5166
|
assert(context.count === this.count);
|
|
6307
5167
|
const source = context.segmentsList;
|
|
6308
5168
|
for (let i = 0; i < source.length; ++i) this.segmentsList.push(source[i]);
|
|
6309
5169
|
}
|
|
6310
|
-
/**
|
|
6311
|
-
* Clears all segments in this context.
|
|
6312
|
-
*/
|
|
6313
5170
|
clear() {
|
|
6314
5171
|
this.segmentsList = [];
|
|
6315
5172
|
}
|
|
6316
|
-
/**
|
|
6317
|
-
* Creates the root fork context.
|
|
6318
|
-
*/
|
|
6319
5173
|
static newRoot(idGenerator) {
|
|
6320
5174
|
const context = new ForkContext(idGenerator, null, 1);
|
|
6321
5175
|
context.add([CodePathSegment.newRoot(idGenerator.next())]);
|
|
6322
5176
|
return context;
|
|
6323
5177
|
}
|
|
6324
|
-
/**
|
|
6325
|
-
* Creates an empty fork context preceded by a given context.
|
|
6326
|
-
*/
|
|
6327
5178
|
static newEmpty(parentContext, forkLeavingPath) {
|
|
6328
5179
|
return new ForkContext(parentContext.idGenerator, parentContext, (forkLeavingPath ? 2 : 1) * parentContext.count);
|
|
6329
5180
|
}
|
|
6330
5181
|
};
|
|
6331
|
-
/**
|
|
6332
|
-
* Adds given segments into the `dest` array.
|
|
6333
|
-
* If the `others` array does not include the given segments, adds to the `all`
|
|
6334
|
-
* array as well.
|
|
6335
|
-
*
|
|
6336
|
-
* This adds only reachable and used segments.
|
|
6337
|
-
*/
|
|
6338
5182
|
function addToReturnedOrThrown(dest, others, all, segments) {
|
|
6339
5183
|
for (let i = 0; i < segments.length; ++i) {
|
|
6340
5184
|
const segment = segments[i];
|
|
@@ -6342,9 +5186,6 @@ function addToReturnedOrThrown(dest, others, all, segments) {
|
|
|
6342
5186
|
if (!others.includes(segment)) all.push(segment);
|
|
6343
5187
|
}
|
|
6344
5188
|
}
|
|
6345
|
-
/**
|
|
6346
|
-
* Gets a loop-context for a `continue` statement.
|
|
6347
|
-
*/
|
|
6348
5189
|
function getContinueContext(state, label) {
|
|
6349
5190
|
if (!label) return state.loopContext;
|
|
6350
5191
|
let context = state.loopContext;
|
|
@@ -6355,9 +5196,6 @@ function getContinueContext(state, label) {
|
|
|
6355
5196
|
/* c8 ignore next */
|
|
6356
5197
|
return null;
|
|
6357
5198
|
}
|
|
6358
|
-
/**
|
|
6359
|
-
* Gets a context for a `break` statement.
|
|
6360
|
-
*/
|
|
6361
5199
|
function getBreakContext(state, label) {
|
|
6362
5200
|
let context = state.breakContext;
|
|
6363
5201
|
while (context) {
|
|
@@ -6367,9 +5205,6 @@ function getBreakContext(state, label) {
|
|
|
6367
5205
|
/* c8 ignore next */
|
|
6368
5206
|
return null;
|
|
6369
5207
|
}
|
|
6370
|
-
/**
|
|
6371
|
-
* Gets a context for a `return` statement.
|
|
6372
|
-
*/
|
|
6373
5208
|
function getReturnContext(state) {
|
|
6374
5209
|
let context = state.tryContext;
|
|
6375
5210
|
while (context) {
|
|
@@ -6378,9 +5213,6 @@ function getReturnContext(state) {
|
|
|
6378
5213
|
}
|
|
6379
5214
|
return state;
|
|
6380
5215
|
}
|
|
6381
|
-
/**
|
|
6382
|
-
* Gets a context for a `throw` statement.
|
|
6383
|
-
*/
|
|
6384
5216
|
function getThrowContext(state) {
|
|
6385
5217
|
let context = state.tryContext;
|
|
6386
5218
|
while (context) {
|
|
@@ -6389,19 +5221,9 @@ function getThrowContext(state) {
|
|
|
6389
5221
|
}
|
|
6390
5222
|
return state;
|
|
6391
5223
|
}
|
|
6392
|
-
/**
|
|
6393
|
-
* Removes a given element from a given array.
|
|
6394
|
-
*/
|
|
6395
5224
|
function remove(xs, x) {
|
|
6396
5225
|
xs.splice(xs.indexOf(x), 1);
|
|
6397
5226
|
}
|
|
6398
|
-
/**
|
|
6399
|
-
* Disconnect given segments.
|
|
6400
|
-
*
|
|
6401
|
-
* This is used in a process for switch statements.
|
|
6402
|
-
* If there is the "default" chunk before other cases, the order is different
|
|
6403
|
-
* between node's and running's.
|
|
6404
|
-
*/
|
|
6405
5227
|
function removeConnection(prevSegments, nextSegments) {
|
|
6406
5228
|
for (let i = 0; i < prevSegments.length; ++i) {
|
|
6407
5229
|
const prevSegment = prevSegments[i];
|
|
@@ -6412,9 +5234,6 @@ function removeConnection(prevSegments, nextSegments) {
|
|
|
6412
5234
|
remove(nextSegment.allPrevSegments, prevSegment);
|
|
6413
5235
|
}
|
|
6414
5236
|
}
|
|
6415
|
-
/**
|
|
6416
|
-
* Creates looping path.
|
|
6417
|
-
*/
|
|
6418
5237
|
function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
|
|
6419
5238
|
const fromSegments = CodePathSegment.flattenUnusedSegments(unflattenedFromSegments);
|
|
6420
5239
|
const toSegments = CodePathSegment.flattenUnusedSegments(unflattenedToSegments);
|
|
@@ -6430,12 +5249,6 @@ function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
|
|
|
6430
5249
|
state.notifyLooped(fromSegment, toSegment);
|
|
6431
5250
|
}
|
|
6432
5251
|
}
|
|
6433
|
-
/**
|
|
6434
|
-
* Finalizes segments of `test` chunk of a ForStatement.
|
|
6435
|
-
*
|
|
6436
|
-
* - Adds `false` paths to paths which are leaving from the loop.
|
|
6437
|
-
* - Sets `true` paths to paths which go to the body.
|
|
6438
|
-
*/
|
|
6439
5252
|
function finalizeTestSegmentsOfFor(context, choiceContext, head) {
|
|
6440
5253
|
if (!choiceContext.processed) {
|
|
6441
5254
|
choiceContext.trueForkContext.add(head);
|
|
@@ -6445,9 +5258,6 @@ function finalizeTestSegmentsOfFor(context, choiceContext, head) {
|
|
|
6445
5258
|
if (context.test !== true) context.brokenForkContext.addAll(choiceContext.falseForkContext);
|
|
6446
5259
|
context.endOfTestSegments = choiceContext.trueForkContext.makeNext(0, -1);
|
|
6447
5260
|
}
|
|
6448
|
-
/**
|
|
6449
|
-
* A class which manages state to analyze code paths.
|
|
6450
|
-
*/
|
|
6451
5261
|
var CodePathState = class {
|
|
6452
5262
|
idGenerator;
|
|
6453
5263
|
notifyLooped;
|
|
@@ -6484,60 +5294,29 @@ var CodePathState = class {
|
|
|
6484
5294
|
returned.add = addToReturnedOrThrown.bind(null, returned, thrown, final);
|
|
6485
5295
|
thrown.add = addToReturnedOrThrown.bind(null, thrown, returned, final);
|
|
6486
5296
|
}
|
|
6487
|
-
/** The head segments. */
|
|
6488
5297
|
get headSegments() {
|
|
6489
5298
|
return this.forkContext.head;
|
|
6490
5299
|
}
|
|
6491
|
-
/**
|
|
6492
|
-
* The parent forking context.
|
|
6493
|
-
* This is used for the root of new forks.
|
|
6494
|
-
*/
|
|
6495
5300
|
get parentForkContext() {
|
|
6496
5301
|
const current = this.forkContext;
|
|
6497
5302
|
return current && current.upper;
|
|
6498
5303
|
}
|
|
6499
|
-
/**
|
|
6500
|
-
* Creates and stacks new forking context.
|
|
6501
|
-
*/
|
|
6502
5304
|
pushForkContext(forkLeavingPath) {
|
|
6503
5305
|
this.forkContext = ForkContext.newEmpty(this.forkContext, forkLeavingPath);
|
|
6504
5306
|
return this.forkContext;
|
|
6505
5307
|
}
|
|
6506
|
-
/**
|
|
6507
|
-
* Pops and merges the last forking context.
|
|
6508
|
-
*/
|
|
6509
5308
|
popForkContext() {
|
|
6510
5309
|
const lastContext = this.forkContext;
|
|
6511
5310
|
this.forkContext = lastContext.upper;
|
|
6512
5311
|
this.forkContext.replaceHead(lastContext.makeNext(0, -1));
|
|
6513
5312
|
return lastContext;
|
|
6514
5313
|
}
|
|
6515
|
-
/**
|
|
6516
|
-
* Creates a new path.
|
|
6517
|
-
*/
|
|
6518
5314
|
forkPath() {
|
|
6519
5315
|
this.forkContext.add(this.parentForkContext.makeNext(-1, -1));
|
|
6520
5316
|
}
|
|
6521
|
-
/**
|
|
6522
|
-
* Creates a bypass path.
|
|
6523
|
-
* This is used for such as IfStatement which does not have "else" chunk.
|
|
6524
|
-
*/
|
|
6525
5317
|
forkBypassPath() {
|
|
6526
5318
|
this.forkContext.add(this.parentForkContext.head);
|
|
6527
5319
|
}
|
|
6528
|
-
/**
|
|
6529
|
-
* Creates a context for ConditionalExpression, LogicalExpression,
|
|
6530
|
-
* AssignmentExpression (logical assignments only), IfStatement,
|
|
6531
|
-
* WhileStatement, DoWhileStatement, or ForStatement.
|
|
6532
|
-
*
|
|
6533
|
-
* @param kind A kind string.
|
|
6534
|
-
* If the new context is LogicalExpression's or AssignmentExpression's,
|
|
6535
|
-
* this is `"&&"` or `"||"` or `"??"`.
|
|
6536
|
-
* If it's IfStatement's or ConditionalExpression's, this is `"test"`.
|
|
6537
|
-
* Otherwise, this is `"loop"`.
|
|
6538
|
-
* @param isForkingAsResult A flag that shows that goes different
|
|
6539
|
-
* paths between `true` and `false`.
|
|
6540
|
-
*/
|
|
6541
5320
|
pushChoiceContext(kind, isForkingAsResult) {
|
|
6542
5321
|
this.choiceContext = {
|
|
6543
5322
|
upper: this.choiceContext,
|
|
@@ -6549,9 +5328,6 @@ var CodePathState = class {
|
|
|
6549
5328
|
processed: false
|
|
6550
5329
|
};
|
|
6551
5330
|
}
|
|
6552
|
-
/**
|
|
6553
|
-
* Pops the last choice context and finalizes it.
|
|
6554
|
-
*/
|
|
6555
5331
|
popChoiceContext() {
|
|
6556
5332
|
const context = this.choiceContext;
|
|
6557
5333
|
this.choiceContext = context.upper;
|
|
@@ -6592,10 +5368,6 @@ var CodePathState = class {
|
|
|
6592
5368
|
forkContext.replaceHead(prevForkContext.makeNext(0, -1));
|
|
6593
5369
|
return context;
|
|
6594
5370
|
}
|
|
6595
|
-
/**
|
|
6596
|
-
* Makes a code path segment of the right-hand operand of a logical
|
|
6597
|
-
* expression.
|
|
6598
|
-
*/
|
|
6599
5371
|
makeLogicalRight() {
|
|
6600
5372
|
const context = this.choiceContext;
|
|
6601
5373
|
const forkContext = this.forkContext;
|
|
@@ -6633,9 +5405,6 @@ var CodePathState = class {
|
|
|
6633
5405
|
forkContext.replaceHead(forkContext.makeNext(-1, -1));
|
|
6634
5406
|
}
|
|
6635
5407
|
}
|
|
6636
|
-
/**
|
|
6637
|
-
* Makes a code path segment of the `if` block.
|
|
6638
|
-
*/
|
|
6639
5408
|
makeIfConsequent() {
|
|
6640
5409
|
const context = this.choiceContext;
|
|
6641
5410
|
const forkContext = this.forkContext;
|
|
@@ -6647,9 +5416,6 @@ var CodePathState = class {
|
|
|
6647
5416
|
context.processed = false;
|
|
6648
5417
|
forkContext.replaceHead(context.trueForkContext.makeNext(0, -1));
|
|
6649
5418
|
}
|
|
6650
|
-
/**
|
|
6651
|
-
* Makes a code path segment of the `else` block.
|
|
6652
|
-
*/
|
|
6653
5419
|
makeIfAlternate() {
|
|
6654
5420
|
const context = this.choiceContext;
|
|
6655
5421
|
const forkContext = this.forkContext;
|
|
@@ -6658,50 +5424,26 @@ var CodePathState = class {
|
|
|
6658
5424
|
context.processed = true;
|
|
6659
5425
|
forkContext.replaceHead(context.falseForkContext.makeNext(0, -1));
|
|
6660
5426
|
}
|
|
6661
|
-
/**
|
|
6662
|
-
* Push a new `ChainExpression` context to the stack.
|
|
6663
|
-
* This method is called on entering to each `ChainExpression` node.
|
|
6664
|
-
* This context is used to count forking in the optional chain then merge
|
|
6665
|
-
* them on exiting from the `ChainExpression` node.
|
|
6666
|
-
*/
|
|
6667
5427
|
pushChainContext() {
|
|
6668
5428
|
this.chainContext = {
|
|
6669
5429
|
upper: this.chainContext,
|
|
6670
5430
|
countChoiceContexts: 0
|
|
6671
5431
|
};
|
|
6672
5432
|
}
|
|
6673
|
-
/**
|
|
6674
|
-
* Pop a `ChainExpression` context from the stack.
|
|
6675
|
-
* This method is called on exiting from each `ChainExpression` node.
|
|
6676
|
-
* This merges all forks of the last optional chaining.
|
|
6677
|
-
*/
|
|
6678
5433
|
popChainContext() {
|
|
6679
5434
|
const context = this.chainContext;
|
|
6680
5435
|
this.chainContext = context.upper;
|
|
6681
5436
|
for (let i = context.countChoiceContexts; i > 0; --i) this.popChoiceContext();
|
|
6682
5437
|
}
|
|
6683
|
-
/**
|
|
6684
|
-
* Create a choice context for optional access.
|
|
6685
|
-
* This method is called on entering to each `(Call|Member)Expression[optional=true]` node.
|
|
6686
|
-
* This creates a choice context as similar to `LogicalExpression[operator="??"]` node.
|
|
6687
|
-
*/
|
|
6688
5438
|
makeOptionalNode() {
|
|
6689
5439
|
if (this.chainContext) {
|
|
6690
5440
|
this.chainContext.countChoiceContexts += 1;
|
|
6691
5441
|
this.pushChoiceContext("??", false);
|
|
6692
5442
|
}
|
|
6693
5443
|
}
|
|
6694
|
-
/**
|
|
6695
|
-
* Create a fork.
|
|
6696
|
-
* This method is called on entering to the `arguments|property` property
|
|
6697
|
-
* of each `(Call|Member)Expression` node.
|
|
6698
|
-
*/
|
|
6699
5444
|
makeOptionalRight() {
|
|
6700
5445
|
if (this.chainContext) this.makeLogicalRight();
|
|
6701
5446
|
}
|
|
6702
|
-
/**
|
|
6703
|
-
* Creates a context object of SwitchStatement and stacks it.
|
|
6704
|
-
*/
|
|
6705
5447
|
pushSwitchContext(hasCase, label) {
|
|
6706
5448
|
this.switchContext = {
|
|
6707
5449
|
upper: this.switchContext,
|
|
@@ -6714,14 +5456,6 @@ var CodePathState = class {
|
|
|
6714
5456
|
};
|
|
6715
5457
|
this.pushBreakContext(true, label);
|
|
6716
5458
|
}
|
|
6717
|
-
/**
|
|
6718
|
-
* Pops the last context of SwitchStatement and finalizes it.
|
|
6719
|
-
*
|
|
6720
|
-
* - Disposes all forking stack for `case` and `default`.
|
|
6721
|
-
* - Creates the next code path segment from `context.brokenForkContext`.
|
|
6722
|
-
* - If the last `SwitchCase` node is not a `default` part, creates a path
|
|
6723
|
-
* to the `default` body.
|
|
6724
|
-
*/
|
|
6725
5459
|
popSwitchContext() {
|
|
6726
5460
|
const context = this.switchContext;
|
|
6727
5461
|
this.switchContext = context.upper;
|
|
@@ -6747,9 +5481,6 @@ var CodePathState = class {
|
|
|
6747
5481
|
for (let i = 0; i < context.countForks; ++i) this.forkContext = this.forkContext.upper;
|
|
6748
5482
|
this.forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
|
|
6749
5483
|
}
|
|
6750
|
-
/**
|
|
6751
|
-
* Makes a code path segment for a `SwitchCase` node.
|
|
6752
|
-
*/
|
|
6753
5484
|
makeSwitchCaseBody(isEmpty, isDefault) {
|
|
6754
5485
|
const context = this.switchContext;
|
|
6755
5486
|
if (!context.hasCase) return;
|
|
@@ -6767,9 +5498,6 @@ var CodePathState = class {
|
|
|
6767
5498
|
context.lastIsDefault = isDefault;
|
|
6768
5499
|
context.countForks += 1;
|
|
6769
5500
|
}
|
|
6770
|
-
/**
|
|
6771
|
-
* Creates a context object of TryStatement and stacks it.
|
|
6772
|
-
*/
|
|
6773
5501
|
pushTryContext(hasFinalizer) {
|
|
6774
5502
|
this.tryContext = {
|
|
6775
5503
|
upper: this.tryContext,
|
|
@@ -6781,9 +5509,6 @@ var CodePathState = class {
|
|
|
6781
5509
|
lastOfCatchIsReachable: false
|
|
6782
5510
|
};
|
|
6783
5511
|
}
|
|
6784
|
-
/**
|
|
6785
|
-
* Pops the last context of TryStatement and finalizes it.
|
|
6786
|
-
*/
|
|
6787
5512
|
popTryContext() {
|
|
6788
5513
|
const context = this.tryContext;
|
|
6789
5514
|
this.tryContext = context.upper;
|
|
@@ -6803,9 +5528,6 @@ var CodePathState = class {
|
|
|
6803
5528
|
this.forkContext.replaceHead(normalSegments);
|
|
6804
5529
|
if (!context.lastOfTryIsReachable && !context.lastOfCatchIsReachable) this.forkContext.makeUnreachable(-1, -1);
|
|
6805
5530
|
}
|
|
6806
|
-
/**
|
|
6807
|
-
* Makes a code path segment for a `catch` block.
|
|
6808
|
-
*/
|
|
6809
5531
|
makeCatchBlock() {
|
|
6810
5532
|
const context = this.tryContext;
|
|
6811
5533
|
const forkContext = this.forkContext;
|
|
@@ -6819,13 +5541,6 @@ var CodePathState = class {
|
|
|
6819
5541
|
this.forkBypassPath();
|
|
6820
5542
|
this.forkContext.add(thrownSegments);
|
|
6821
5543
|
}
|
|
6822
|
-
/**
|
|
6823
|
-
* Makes a code path segment for a `finally` block.
|
|
6824
|
-
*
|
|
6825
|
-
* In the `finally` block, parallel paths are created. The parallel paths
|
|
6826
|
-
* are used as leaving-paths. The leaving-paths are paths from `return`
|
|
6827
|
-
* statements and `throw` statements in a `try` block or a `catch` block.
|
|
6828
|
-
*/
|
|
6829
5544
|
makeFinallyBlock() {
|
|
6830
5545
|
const context = this.tryContext;
|
|
6831
5546
|
let forkContext = this.forkContext;
|
|
@@ -6849,10 +5564,6 @@ var CodePathState = class {
|
|
|
6849
5564
|
this.pushForkContext(true);
|
|
6850
5565
|
this.forkContext.add(segments);
|
|
6851
5566
|
}
|
|
6852
|
-
/**
|
|
6853
|
-
* Makes a code path segment from the first throwable node to the `catch`
|
|
6854
|
-
* block or the `finally` block.
|
|
6855
|
-
*/
|
|
6856
5567
|
makeFirstThrowablePathInTryBlock() {
|
|
6857
5568
|
const forkContext = this.forkContext;
|
|
6858
5569
|
if (!forkContext.reachable) return;
|
|
@@ -6861,9 +5572,6 @@ var CodePathState = class {
|
|
|
6861
5572
|
context.thrownForkContext.add(forkContext.head);
|
|
6862
5573
|
forkContext.replaceHead(forkContext.makeNext(-1, -1));
|
|
6863
5574
|
}
|
|
6864
|
-
/**
|
|
6865
|
-
* Creates a context object of a loop statement and stacks it.
|
|
6866
|
-
*/
|
|
6867
5575
|
pushLoopContext(type, label) {
|
|
6868
5576
|
const forkContext = this.forkContext;
|
|
6869
5577
|
const breakContext = this.pushBreakContext(true, label);
|
|
@@ -6923,9 +5631,6 @@ var CodePathState = class {
|
|
|
6923
5631
|
default: throw new Error(`unknown type: "${type}"`);
|
|
6924
5632
|
}
|
|
6925
5633
|
}
|
|
6926
|
-
/**
|
|
6927
|
-
* Pops the last context of a loop statement and finalizes it.
|
|
6928
|
-
*/
|
|
6929
5634
|
popLoopContext() {
|
|
6930
5635
|
const context = this.loopContext;
|
|
6931
5636
|
this.loopContext = context.upper;
|
|
@@ -6958,9 +5663,6 @@ var CodePathState = class {
|
|
|
6958
5663
|
if (brokenForkContext.empty) forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
6959
5664
|
else forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
|
|
6960
5665
|
}
|
|
6961
|
-
/**
|
|
6962
|
-
* Makes a code path segment for the test part of a WhileStatement.
|
|
6963
|
-
*/
|
|
6964
5666
|
makeWhileTest(test) {
|
|
6965
5667
|
const context = this.loopContext;
|
|
6966
5668
|
const forkContext = this.forkContext;
|
|
@@ -6969,9 +5671,6 @@ var CodePathState = class {
|
|
|
6969
5671
|
context.continueDestSegments = testSegments;
|
|
6970
5672
|
forkContext.replaceHead(testSegments);
|
|
6971
5673
|
}
|
|
6972
|
-
/**
|
|
6973
|
-
* Makes a code path segment for the body part of a WhileStatement.
|
|
6974
|
-
*/
|
|
6975
5674
|
makeWhileBody() {
|
|
6976
5675
|
const context = this.loopContext;
|
|
6977
5676
|
const choiceContext = this.choiceContext;
|
|
@@ -6983,9 +5682,6 @@ var CodePathState = class {
|
|
|
6983
5682
|
if (context.test !== true) context.brokenForkContext.addAll(choiceContext.falseForkContext);
|
|
6984
5683
|
forkContext.replaceHead(choiceContext.trueForkContext.makeNext(0, -1));
|
|
6985
5684
|
}
|
|
6986
|
-
/**
|
|
6987
|
-
* Makes a code path segment for the body part of a DoWhileStatement.
|
|
6988
|
-
*/
|
|
6989
5685
|
makeDoWhileBody() {
|
|
6990
5686
|
const context = this.loopContext;
|
|
6991
5687
|
const forkContext = this.forkContext;
|
|
@@ -6993,9 +5689,6 @@ var CodePathState = class {
|
|
|
6993
5689
|
context.entrySegments = bodySegments;
|
|
6994
5690
|
forkContext.replaceHead(bodySegments);
|
|
6995
5691
|
}
|
|
6996
|
-
/**
|
|
6997
|
-
* Makes a code path segment for the test part of a DoWhileStatement.
|
|
6998
|
-
*/
|
|
6999
5692
|
makeDoWhileTest(test) {
|
|
7000
5693
|
const context = this.loopContext;
|
|
7001
5694
|
const forkContext = this.forkContext;
|
|
@@ -7006,9 +5699,6 @@ var CodePathState = class {
|
|
|
7006
5699
|
forkContext.replaceHead(testSegments);
|
|
7007
5700
|
}
|
|
7008
5701
|
}
|
|
7009
|
-
/**
|
|
7010
|
-
* Makes a code path segment for the test part of a ForStatement.
|
|
7011
|
-
*/
|
|
7012
5702
|
makeForTest(test) {
|
|
7013
5703
|
const context = this.loopContext;
|
|
7014
5704
|
const forkContext = this.forkContext;
|
|
@@ -7019,9 +5709,6 @@ var CodePathState = class {
|
|
|
7019
5709
|
context.continueDestSegments = context.testSegments = testSegments;
|
|
7020
5710
|
forkContext.replaceHead(testSegments);
|
|
7021
5711
|
}
|
|
7022
|
-
/**
|
|
7023
|
-
* Makes a code path segment for the update part of a ForStatement.
|
|
7024
|
-
*/
|
|
7025
5712
|
makeForUpdate() {
|
|
7026
5713
|
const context = this.loopContext;
|
|
7027
5714
|
const choiceContext = this.choiceContext;
|
|
@@ -7032,9 +5719,6 @@ var CodePathState = class {
|
|
|
7032
5719
|
context.continueDestSegments = context.updateSegments = updateSegments;
|
|
7033
5720
|
forkContext.replaceHead(updateSegments);
|
|
7034
5721
|
}
|
|
7035
|
-
/**
|
|
7036
|
-
* Makes a code path segment for the body part of a ForStatement.
|
|
7037
|
-
*/
|
|
7038
5722
|
makeForBody() {
|
|
7039
5723
|
const context = this.loopContext;
|
|
7040
5724
|
const choiceContext = this.choiceContext;
|
|
@@ -7054,10 +5738,6 @@ var CodePathState = class {
|
|
|
7054
5738
|
context.continueDestSegments = context.continueDestSegments || bodySegments;
|
|
7055
5739
|
forkContext.replaceHead(bodySegments);
|
|
7056
5740
|
}
|
|
7057
|
-
/**
|
|
7058
|
-
* Makes a code path segment for the left part of a ForInStatement and a
|
|
7059
|
-
* ForOfStatement.
|
|
7060
|
-
*/
|
|
7061
5741
|
makeForInOfLeft() {
|
|
7062
5742
|
const context = this.loopContext;
|
|
7063
5743
|
const forkContext = this.forkContext;
|
|
@@ -7066,10 +5746,6 @@ var CodePathState = class {
|
|
|
7066
5746
|
context.leftSegments = context.continueDestSegments = leftSegments;
|
|
7067
5747
|
forkContext.replaceHead(leftSegments);
|
|
7068
5748
|
}
|
|
7069
|
-
/**
|
|
7070
|
-
* Makes a code path segment for the right part of a ForInStatement and a
|
|
7071
|
-
* ForOfStatement.
|
|
7072
|
-
*/
|
|
7073
5749
|
makeForInOfRight() {
|
|
7074
5750
|
const context = this.loopContext;
|
|
7075
5751
|
const forkContext = this.forkContext;
|
|
@@ -7079,10 +5755,6 @@ var CodePathState = class {
|
|
|
7079
5755
|
context.endOfLeftSegments = forkContext.head;
|
|
7080
5756
|
forkContext.replaceHead(rightSegments);
|
|
7081
5757
|
}
|
|
7082
|
-
/**
|
|
7083
|
-
* Makes a code path segment for the body part of a ForInStatement and a
|
|
7084
|
-
* ForOfStatement.
|
|
7085
|
-
*/
|
|
7086
5758
|
makeForInOfBody() {
|
|
7087
5759
|
const context = this.loopContext;
|
|
7088
5760
|
const forkContext = this.forkContext;
|
|
@@ -7093,9 +5765,6 @@ var CodePathState = class {
|
|
|
7093
5765
|
context.brokenForkContext.add(forkContext.head);
|
|
7094
5766
|
forkContext.replaceHead(bodySegments);
|
|
7095
5767
|
}
|
|
7096
|
-
/**
|
|
7097
|
-
* Creates new context for BreakStatement.
|
|
7098
|
-
*/
|
|
7099
5768
|
pushBreakContext(breakable, label) {
|
|
7100
5769
|
this.breakContext = {
|
|
7101
5770
|
upper: this.breakContext,
|
|
@@ -7105,9 +5774,6 @@ var CodePathState = class {
|
|
|
7105
5774
|
};
|
|
7106
5775
|
return this.breakContext;
|
|
7107
5776
|
}
|
|
7108
|
-
/**
|
|
7109
|
-
* Removes the top item of the break context stack.
|
|
7110
|
-
*/
|
|
7111
5777
|
popBreakContext() {
|
|
7112
5778
|
const context = this.breakContext;
|
|
7113
5779
|
const forkContext = this.forkContext;
|
|
@@ -7121,12 +5787,6 @@ var CodePathState = class {
|
|
|
7121
5787
|
}
|
|
7122
5788
|
return context;
|
|
7123
5789
|
}
|
|
7124
|
-
/**
|
|
7125
|
-
* Makes a path for a `break` statement.
|
|
7126
|
-
*
|
|
7127
|
-
* It registers the head segment to a context of `break`.
|
|
7128
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
7129
|
-
*/
|
|
7130
5790
|
makeBreak(label) {
|
|
7131
5791
|
const forkContext = this.forkContext;
|
|
7132
5792
|
if (!forkContext.reachable) return;
|
|
@@ -7135,12 +5795,6 @@ var CodePathState = class {
|
|
|
7135
5795
|
/* c8 ignore next */
|
|
7136
5796
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
7137
5797
|
}
|
|
7138
|
-
/**
|
|
7139
|
-
* Makes a path for a `continue` statement.
|
|
7140
|
-
*
|
|
7141
|
-
* It makes a looping path.
|
|
7142
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
7143
|
-
*/
|
|
7144
5798
|
makeContinue(label) {
|
|
7145
5799
|
const forkContext = this.forkContext;
|
|
7146
5800
|
if (!forkContext.reachable) return;
|
|
@@ -7153,12 +5807,6 @@ var CodePathState = class {
|
|
|
7153
5807
|
}
|
|
7154
5808
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
7155
5809
|
}
|
|
7156
|
-
/**
|
|
7157
|
-
* Makes a path for a `return` statement.
|
|
7158
|
-
*
|
|
7159
|
-
* It registers the head segment to a context of `return`.
|
|
7160
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
7161
|
-
*/
|
|
7162
5810
|
makeReturn() {
|
|
7163
5811
|
const forkContext = this.forkContext;
|
|
7164
5812
|
if (forkContext.reachable) {
|
|
@@ -7166,12 +5814,6 @@ var CodePathState = class {
|
|
|
7166
5814
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
7167
5815
|
}
|
|
7168
5816
|
}
|
|
7169
|
-
/**
|
|
7170
|
-
* Makes a path for a `throw` statement.
|
|
7171
|
-
*
|
|
7172
|
-
* It registers the head segment to a context of `throw`.
|
|
7173
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
7174
|
-
*/
|
|
7175
5817
|
makeThrow() {
|
|
7176
5818
|
const forkContext = this.forkContext;
|
|
7177
5819
|
if (forkContext.reachable) {
|
|
@@ -7179,17 +5821,11 @@ var CodePathState = class {
|
|
|
7179
5821
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
7180
5822
|
}
|
|
7181
5823
|
}
|
|
7182
|
-
/**
|
|
7183
|
-
* Makes the final path.
|
|
7184
|
-
*/
|
|
7185
5824
|
makeFinal() {
|
|
7186
5825
|
const segments = this.currentSegments;
|
|
7187
5826
|
if (segments.length > 0 && segments[0].reachable) this.returnedForkContext.add(segments);
|
|
7188
5827
|
}
|
|
7189
5828
|
};
|
|
7190
|
-
/**
|
|
7191
|
-
* A generator for unique ids.
|
|
7192
|
-
*/
|
|
7193
5829
|
var IdGenerator = class {
|
|
7194
5830
|
prefix;
|
|
7195
5831
|
n;
|
|
@@ -7197,9 +5833,6 @@ var IdGenerator = class {
|
|
|
7197
5833
|
this.prefix = String(prefix);
|
|
7198
5834
|
this.n = 0;
|
|
7199
5835
|
}
|
|
7200
|
-
/**
|
|
7201
|
-
* Generates id.
|
|
7202
|
-
*/
|
|
7203
5836
|
next() {
|
|
7204
5837
|
this.n = 1 + this.n | 0;
|
|
7205
5838
|
/* c8 ignore start */
|
|
@@ -7207,22 +5840,11 @@ var IdGenerator = class {
|
|
|
7207
5840
|
return this.prefix + this.n;
|
|
7208
5841
|
}
|
|
7209
5842
|
};
|
|
7210
|
-
/**
|
|
7211
|
-
* A code path.
|
|
7212
|
-
*/
|
|
7213
5843
|
var CodePath = class {
|
|
7214
|
-
/** The identifier of this code path. */
|
|
7215
5844
|
id;
|
|
7216
|
-
/**
|
|
7217
|
-
* The reason that this code path was started. May be "program",
|
|
7218
|
-
* "function", "class-field-initializer", or "class-static-block".
|
|
7219
|
-
*/
|
|
7220
5845
|
origin;
|
|
7221
|
-
/** The code path of the upper function scope. */
|
|
7222
5846
|
upper;
|
|
7223
|
-
/** The code paths of nested function scopes. */
|
|
7224
5847
|
childCodePaths;
|
|
7225
|
-
/** Internal state for the code path. */
|
|
7226
5848
|
internal;
|
|
7227
5849
|
constructor({ id, origin, upper, onLooped }) {
|
|
7228
5850
|
this.id = id;
|
|
@@ -7232,48 +5854,24 @@ var CodePath = class {
|
|
|
7232
5854
|
this.internal = new CodePathState(new IdGenerator(`${id}_`), onLooped);
|
|
7233
5855
|
if (upper) upper.childCodePaths.push(this);
|
|
7234
5856
|
}
|
|
7235
|
-
/**
|
|
7236
|
-
* Gets the state of a given code path.
|
|
7237
|
-
*/
|
|
7238
5857
|
static getState(codePath) {
|
|
7239
5858
|
return codePath.internal;
|
|
7240
5859
|
}
|
|
7241
|
-
/** The initial code path segment. */
|
|
7242
5860
|
get initialSegment() {
|
|
7243
5861
|
return this.internal.initialSegment;
|
|
7244
5862
|
}
|
|
7245
|
-
/**
|
|
7246
|
-
* Final code path segments.
|
|
7247
|
-
* This array is a mix of `returnedSegments` and `thrownSegments`.
|
|
7248
|
-
*/
|
|
7249
5863
|
get finalSegments() {
|
|
7250
5864
|
return this.internal.finalSegments;
|
|
7251
5865
|
}
|
|
7252
|
-
/**
|
|
7253
|
-
* Final code path segments which is with `return` statements.
|
|
7254
|
-
* This array contains the last path segment if it's reachable.
|
|
7255
|
-
* Since the reachable last path returns `undefined`.
|
|
7256
|
-
*/
|
|
7257
5866
|
get returnedSegments() {
|
|
7258
5867
|
return this.internal.returnedForkContext;
|
|
7259
5868
|
}
|
|
7260
|
-
/** Final code path segments which is with `throw` statements. */
|
|
7261
5869
|
get thrownSegments() {
|
|
7262
5870
|
return this.internal.thrownForkContext;
|
|
7263
5871
|
}
|
|
7264
|
-
/** Current code path segments. */
|
|
7265
5872
|
get currentSegments() {
|
|
7266
5873
|
return this.internal.currentSegments;
|
|
7267
5874
|
}
|
|
7268
|
-
/**
|
|
7269
|
-
* Traverses all segments in this code path.
|
|
7270
|
-
*
|
|
7271
|
-
* This method enumerates segments in order from the head.
|
|
7272
|
-
*
|
|
7273
|
-
* The `controller` object has two methods:
|
|
7274
|
-
* - `controller.skip()` - Skip the following segments in this branch.
|
|
7275
|
-
* - `controller.break()` - Skip all following segments.
|
|
7276
|
-
*/
|
|
7277
5875
|
traverseSegments(options, callback) {
|
|
7278
5876
|
let resolvedOptions;
|
|
7279
5877
|
let resolvedCallback;
|
|
@@ -7339,45 +5937,23 @@ var CodePath = class {
|
|
|
7339
5937
|
}
|
|
7340
5938
|
};
|
|
7341
5939
|
const breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/u;
|
|
7342
|
-
/**
|
|
7343
|
-
* Checks whether or not a given node is a `case` node (not `default` node).
|
|
7344
|
-
*/
|
|
7345
5940
|
function isCaseNode(node) {
|
|
7346
5941
|
return Boolean(node.test);
|
|
7347
5942
|
}
|
|
7348
|
-
/**
|
|
7349
|
-
* Checks if a given node appears as the value of a PropertyDefinition node.
|
|
7350
|
-
*/
|
|
7351
5943
|
function isPropertyDefinitionValue(node) {
|
|
7352
5944
|
const parent = node.parent;
|
|
7353
5945
|
return parent && parent.type === "PropertyDefinition" && parent.value === node;
|
|
7354
5946
|
}
|
|
7355
|
-
/**
|
|
7356
|
-
* Checks whether the given logical operator is taken into account for the code
|
|
7357
|
-
* path analysis.
|
|
7358
|
-
*/
|
|
7359
5947
|
function isHandledLogicalOperator(operator) {
|
|
7360
5948
|
return operator === "&&" || operator === "||" || operator === "??";
|
|
7361
5949
|
}
|
|
7362
|
-
/**
|
|
7363
|
-
* Checks whether the given assignment operator is a logical assignment operator.
|
|
7364
|
-
* Logical assignments are taken into account for the code path analysis
|
|
7365
|
-
* because of their short-circuiting semantics.
|
|
7366
|
-
*/
|
|
7367
5950
|
function isLogicalAssignmentOperator(operator) {
|
|
7368
5951
|
return operator === "&&=" || operator === "||=" || operator === "??=";
|
|
7369
5952
|
}
|
|
7370
|
-
/**
|
|
7371
|
-
* Gets the label if the parent node of a given node is a LabeledStatement.
|
|
7372
|
-
*/
|
|
7373
5953
|
function getLabel(node) {
|
|
7374
5954
|
if (node.parent.type === "LabeledStatement") return node.parent.label.name;
|
|
7375
5955
|
return null;
|
|
7376
5956
|
}
|
|
7377
|
-
/**
|
|
7378
|
-
* Checks whether or not a given logical expression node goes different path
|
|
7379
|
-
* between the `true` case and the `false` case.
|
|
7380
|
-
*/
|
|
7381
5957
|
function isForkingByTrueOrFalse(node) {
|
|
7382
5958
|
const parent = node.parent;
|
|
7383
5959
|
switch (parent.type) {
|
|
@@ -7391,21 +5967,9 @@ function isForkingByTrueOrFalse(node) {
|
|
|
7391
5967
|
default: return false;
|
|
7392
5968
|
}
|
|
7393
5969
|
}
|
|
7394
|
-
/**
|
|
7395
|
-
* Gets the boolean value of a given literal node.
|
|
7396
|
-
*
|
|
7397
|
-
* This is used to detect infinity loops (ex: `while (true) {}`).
|
|
7398
|
-
* Statements preceded by an infinity loop are unreachable if the loop didn't
|
|
7399
|
-
* have any `break` statement.
|
|
7400
|
-
*/
|
|
7401
5970
|
function getBooleanValueIfSimpleConstant(node) {
|
|
7402
5971
|
if (node.type === "Literal") return Boolean(node.value);
|
|
7403
5972
|
}
|
|
7404
|
-
/**
|
|
7405
|
-
* Checks that a given identifier node is a reference or not.
|
|
7406
|
-
*
|
|
7407
|
-
* This is used to detect the first throwable node in a `try` block.
|
|
7408
|
-
*/
|
|
7409
5973
|
function isIdentifierReference(node) {
|
|
7410
5974
|
const parent = node.parent;
|
|
7411
5975
|
switch (parent.type) {
|
|
@@ -7433,15 +5997,6 @@ function isIdentifierReference(node) {
|
|
|
7433
5997
|
default: return true;
|
|
7434
5998
|
}
|
|
7435
5999
|
}
|
|
7436
|
-
/**
|
|
7437
|
-
* Updates the current segment with the head segment.
|
|
7438
|
-
* This is similar to local branches and tracking branches of git.
|
|
7439
|
-
*
|
|
7440
|
-
* To separate the current and the head is in order to not make useless segments.
|
|
7441
|
-
*
|
|
7442
|
-
* In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
|
|
7443
|
-
* events are fired.
|
|
7444
|
-
*/
|
|
7445
6000
|
function forwardCurrentToHead(analyzer, node) {
|
|
7446
6001
|
const codePath = analyzer.codePath;
|
|
7447
6002
|
const state = CodePath.getState(codePath);
|
|
@@ -7468,10 +6023,6 @@ function forwardCurrentToHead(analyzer, node) {
|
|
|
7468
6023
|
}
|
|
7469
6024
|
}
|
|
7470
6025
|
}
|
|
7471
|
-
/**
|
|
7472
|
-
* Updates the current segment with empty.
|
|
7473
|
-
* This is called at the last of functions or the program.
|
|
7474
|
-
*/
|
|
7475
6026
|
function leaveFromCurrentSegment(analyzer, node) {
|
|
7476
6027
|
const state = CodePath.getState(analyzer.codePath);
|
|
7477
6028
|
const currentSegments = state.currentSegments;
|
|
@@ -7481,13 +6032,6 @@ function leaveFromCurrentSegment(analyzer, node) {
|
|
|
7481
6032
|
}
|
|
7482
6033
|
state.currentSegments = [];
|
|
7483
6034
|
}
|
|
7484
|
-
/**
|
|
7485
|
-
* Updates the code path due to the position of a given node in the parent node
|
|
7486
|
-
* thereof.
|
|
7487
|
-
*
|
|
7488
|
-
* For example, if the node is `parent.consequent`, this creates a fork from the
|
|
7489
|
-
* current path.
|
|
7490
|
-
*/
|
|
7491
6035
|
function preprocess(analyzer, node) {
|
|
7492
6036
|
const codePath = analyzer.codePath;
|
|
7493
6037
|
const state = CodePath.getState(codePath);
|
|
@@ -7552,17 +6096,10 @@ function preprocess(analyzer, node) {
|
|
|
7552
6096
|
}
|
|
7553
6097
|
}
|
|
7554
6098
|
}
|
|
7555
|
-
/**
|
|
7556
|
-
* Updates the code path due to the type of a given node in entering.
|
|
7557
|
-
*/
|
|
7558
6099
|
function processCodePathToEnter(analyzer, node) {
|
|
7559
6100
|
let codePath = analyzer.codePath;
|
|
7560
6101
|
let state = codePath && CodePath.getState(codePath);
|
|
7561
6102
|
const parent = node.parent;
|
|
7562
|
-
/**
|
|
7563
|
-
* Creates a new code path and trigger the onCodePathStart event
|
|
7564
|
-
* based on the currently selected node.
|
|
7565
|
-
*/
|
|
7566
6103
|
function startCodePath(origin) {
|
|
7567
6104
|
if (codePath) forwardCurrentToHead(analyzer, node);
|
|
7568
6105
|
codePath = analyzer.codePath = new CodePath({
|
|
@@ -7628,9 +6165,6 @@ function processCodePathToEnter(analyzer, node) {
|
|
|
7628
6165
|
}
|
|
7629
6166
|
forwardCurrentToHead(analyzer, node);
|
|
7630
6167
|
}
|
|
7631
|
-
/**
|
|
7632
|
-
* Updates the code path due to the type of a given node in leaving.
|
|
7633
|
-
*/
|
|
7634
6168
|
function processCodePathToExit(analyzer, node) {
|
|
7635
6169
|
const codePath = analyzer.codePath;
|
|
7636
6170
|
const state = CodePath.getState(codePath);
|
|
@@ -7706,9 +6240,6 @@ function processCodePathToExit(analyzer, node) {
|
|
|
7706
6240
|
}
|
|
7707
6241
|
if (!dontForward) forwardCurrentToHead(analyzer, node);
|
|
7708
6242
|
}
|
|
7709
|
-
/**
|
|
7710
|
-
* Updates the code path to finalize the current code path.
|
|
7711
|
-
*/
|
|
7712
6243
|
function postprocess(analyzer, node) {
|
|
7713
6244
|
function endCodePath() {
|
|
7714
6245
|
const codePath = analyzer.codePath;
|
|
@@ -7731,10 +6262,6 @@ function postprocess(analyzer, node) {
|
|
|
7731
6262
|
}
|
|
7732
6263
|
if (isPropertyDefinitionValue(node)) endCodePath();
|
|
7733
6264
|
}
|
|
7734
|
-
/**
|
|
7735
|
-
* The class to analyze code paths.
|
|
7736
|
-
* This class implements the EventGenerator interface.
|
|
7737
|
-
*/
|
|
7738
6265
|
var CodePathAnalyzer = class {
|
|
7739
6266
|
emitter;
|
|
7740
6267
|
codePath;
|
|
@@ -7750,30 +6277,18 @@ var CodePathAnalyzer = class {
|
|
|
7750
6277
|
this.currentNode = null;
|
|
7751
6278
|
this.onLooped = this._onLooped.bind(this);
|
|
7752
6279
|
}
|
|
7753
|
-
/**
|
|
7754
|
-
* Does the process to enter a given AST node.
|
|
7755
|
-
* This updates state of analysis and calls `enterNode` of the wrapped.
|
|
7756
|
-
*/
|
|
7757
6280
|
enterNode(node) {
|
|
7758
6281
|
this.currentNode = node;
|
|
7759
6282
|
if (node.parent) preprocess(this, node);
|
|
7760
6283
|
processCodePathToEnter(this, node);
|
|
7761
6284
|
this.currentNode = null;
|
|
7762
6285
|
}
|
|
7763
|
-
/**
|
|
7764
|
-
* Does the process to leave a given AST node.
|
|
7765
|
-
* This updates state of analysis and calls `leaveNode` of the wrapped.
|
|
7766
|
-
*/
|
|
7767
6286
|
leaveNode(node) {
|
|
7768
6287
|
this.currentNode = node;
|
|
7769
6288
|
processCodePathToExit(this, node);
|
|
7770
6289
|
postprocess(this, node);
|
|
7771
6290
|
this.currentNode = null;
|
|
7772
6291
|
}
|
|
7773
|
-
/**
|
|
7774
|
-
* This is called on a code path looped.
|
|
7775
|
-
* Then this raises a looped event.
|
|
7776
|
-
*/
|
|
7777
6292
|
_onLooped(fromSegment, toSegment) {
|
|
7778
6293
|
if (fromSegment.reachable && toSegment.reachable) this.emitter.emit("onCodePathSegmentLoop", fromSegment, toSegment, this.currentNode);
|
|
7779
6294
|
}
|
|
@@ -7781,17 +6296,9 @@ var CodePathAnalyzer = class {
|
|
|
7781
6296
|
|
|
7782
6297
|
//#endregion
|
|
7783
6298
|
//#region src/rules/rules-of-hooks/rules-of-hooks.ts
|
|
7784
|
-
/**
|
|
7785
|
-
* Catch all identifiers that begin with "use" followed by an uppercase Latin
|
|
7786
|
-
* character to exclude identifiers like "user".
|
|
7787
|
-
*/
|
|
7788
6299
|
function isHookName(s) {
|
|
7789
6300
|
return s === "use" || /^use[A-Z0-9]/.test(s);
|
|
7790
6301
|
}
|
|
7791
|
-
/**
|
|
7792
|
-
* We consider hooks to be a hook name identifier or a member expression
|
|
7793
|
-
* containing a hook name.
|
|
7794
|
-
*/
|
|
7795
6302
|
function isHook(node) {
|
|
7796
6303
|
const expr = Extract.unwrap(node);
|
|
7797
6304
|
if (expr.type === "Identifier") return isHookName(expr.name);
|
|
@@ -7800,27 +6307,15 @@ function isHook(node) {
|
|
|
7800
6307
|
return obj.type === "Identifier" && /^[A-Z].*/.test(obj.name);
|
|
7801
6308
|
} else return false;
|
|
7802
6309
|
}
|
|
7803
|
-
/**
|
|
7804
|
-
* Checks if the node is a React component name. React component names must
|
|
7805
|
-
* always start with an uppercase letter.
|
|
7806
|
-
*/
|
|
7807
6310
|
function isComponentName(node) {
|
|
7808
6311
|
return node.type === "Identifier" && /^[A-Z]/.test(node.name);
|
|
7809
6312
|
}
|
|
7810
6313
|
function isReactFunction(node, functionName) {
|
|
7811
6314
|
return "name" in node && node.name === functionName || node.type === "MemberExpression" && "name" in node.object && node.object.name === "React" && "name" in node.property && node.property.name === functionName;
|
|
7812
6315
|
}
|
|
7813
|
-
/**
|
|
7814
|
-
* Checks if the node is a callback argument of forwardRef. This render function
|
|
7815
|
-
* should follow the rules of hooks.
|
|
7816
|
-
*/
|
|
7817
6316
|
function isForwardRefCallback(node) {
|
|
7818
6317
|
return !!(node.parent && "callee" in node.parent && node.parent.callee && isReactFunction(node.parent.callee, "forwardRef"));
|
|
7819
6318
|
}
|
|
7820
|
-
/**
|
|
7821
|
-
* Checks if the node is a callback argument of React.memo. This anonymous
|
|
7822
|
-
* functional component should follow the rules of hooks.
|
|
7823
|
-
*/
|
|
7824
6319
|
function isMemoCallback(node) {
|
|
7825
6320
|
return !!(node.parent && "callee" in node.parent && node.parent.callee && isReactFunction(node.parent.callee, "memo"));
|
|
7826
6321
|
}
|
|
@@ -7899,17 +6394,11 @@ const rule = {
|
|
|
7899
6394
|
}
|
|
7900
6395
|
}
|
|
7901
6396
|
}
|
|
7902
|
-
/**
|
|
7903
|
-
* SourceCode that also works down to ESLint 3.0.0
|
|
7904
|
-
*/
|
|
7905
6397
|
const getSourceCode = typeof context.getSourceCode === "function" ? () => {
|
|
7906
6398
|
return context.getSourceCode();
|
|
7907
6399
|
} : () => {
|
|
7908
6400
|
return context.sourceCode;
|
|
7909
6401
|
};
|
|
7910
|
-
/**
|
|
7911
|
-
* SourceCode#getScope that also works down to ESLint 3.0.0
|
|
7912
|
-
*/
|
|
7913
6402
|
const getScope = typeof context.getScope === "function" ? () => {
|
|
7914
6403
|
return context.getScope();
|
|
7915
6404
|
} : (node) => {
|
|
@@ -7929,27 +6418,6 @@ const rule = {
|
|
|
7929
6418
|
if (reactHooksMap?.size === 0) return;
|
|
7930
6419
|
else if (typeof reactHooksMap === "undefined") throw new Error("Unexpected undefined reactHooksMap");
|
|
7931
6420
|
const cyclic = /* @__PURE__ */ new Set();
|
|
7932
|
-
/**
|
|
7933
|
-
* Count the number of code paths from the start of the function to this
|
|
7934
|
-
* segment. For example:
|
|
7935
|
-
*
|
|
7936
|
-
* ```js
|
|
7937
|
-
* function MyComponent() {
|
|
7938
|
-
* if (condition) {
|
|
7939
|
-
* // Segment 1
|
|
7940
|
-
* } else {
|
|
7941
|
-
* // Segment 2
|
|
7942
|
-
* }
|
|
7943
|
-
* // Segment 3
|
|
7944
|
-
* }
|
|
7945
|
-
* ```
|
|
7946
|
-
*
|
|
7947
|
-
* Segments 1 and 2 have one path to the beginning of `MyComponent` and
|
|
7948
|
-
* segment 3 has two paths to the beginning of `MyComponent` since we
|
|
7949
|
-
* could have either taken the path of segment 1 or segment 2.
|
|
7950
|
-
*
|
|
7951
|
-
* Populates `cyclic` with cyclic segments.
|
|
7952
|
-
*/
|
|
7953
6421
|
function countPathsFromStart(segment, pathHistory) {
|
|
7954
6422
|
const { cache } = countPathsFromStart;
|
|
7955
6423
|
let paths = cache.get(segment.id);
|
|
@@ -7972,27 +6440,6 @@ const rule = {
|
|
|
7972
6440
|
else cache.set(segment.id, paths);
|
|
7973
6441
|
return paths;
|
|
7974
6442
|
}
|
|
7975
|
-
/**
|
|
7976
|
-
* Count the number of code paths from this segment to the end of the
|
|
7977
|
-
* function. For example:
|
|
7978
|
-
*
|
|
7979
|
-
* ```js
|
|
7980
|
-
* function MyComponent() {
|
|
7981
|
-
* // Segment 1
|
|
7982
|
-
* if (condition) {
|
|
7983
|
-
* // Segment 2
|
|
7984
|
-
* } else {
|
|
7985
|
-
* // Segment 3
|
|
7986
|
-
* }
|
|
7987
|
-
* }
|
|
7988
|
-
* ```
|
|
7989
|
-
*
|
|
7990
|
-
* Segments 2 and 3 have one path to the end of `MyComponent` and
|
|
7991
|
-
* segment 1 has two paths to the end of `MyComponent` since we could
|
|
7992
|
-
* either take the path of segment 1 or segment 2.
|
|
7993
|
-
*
|
|
7994
|
-
* Populates `cyclic` with cyclic segments.
|
|
7995
|
-
*/
|
|
7996
6443
|
function countPathsToEnd(segment, pathHistory) {
|
|
7997
6444
|
const { cache } = countPathsToEnd;
|
|
7998
6445
|
let paths = cache.get(segment.id);
|
|
@@ -8014,27 +6461,6 @@ const rule = {
|
|
|
8014
6461
|
cache.set(segment.id, paths);
|
|
8015
6462
|
return paths;
|
|
8016
6463
|
}
|
|
8017
|
-
/**
|
|
8018
|
-
* Gets the shortest path length to the start of a code path.
|
|
8019
|
-
* For example:
|
|
8020
|
-
*
|
|
8021
|
-
* ```js
|
|
8022
|
-
* function MyComponent() {
|
|
8023
|
-
* if (condition) {
|
|
8024
|
-
* // Segment 1
|
|
8025
|
-
* }
|
|
8026
|
-
* // Segment 2
|
|
8027
|
-
* }
|
|
8028
|
-
* ```
|
|
8029
|
-
*
|
|
8030
|
-
* There is only one path from segment 1 to the code path start. Its
|
|
8031
|
-
* length is one so that is the shortest path.
|
|
8032
|
-
*
|
|
8033
|
-
* There are two paths from segment 2 to the code path start. One
|
|
8034
|
-
* through segment 1 with a length of two and another directly to the
|
|
8035
|
-
* start with a length of one. The shortest path has a length of one
|
|
8036
|
-
* so we would return that.
|
|
8037
|
-
*/
|
|
8038
6464
|
function shortestPathLengthToStart(segment) {
|
|
8039
6465
|
const { cache } = shortestPathLengthToStart;
|
|
8040
6466
|
let length = cache.get(segment.id);
|
|
@@ -8178,13 +6604,6 @@ const rule = {
|
|
|
8178
6604
|
};
|
|
8179
6605
|
}
|
|
8180
6606
|
};
|
|
8181
|
-
/**
|
|
8182
|
-
* Gets the static name of a function AST node. For function declarations it is
|
|
8183
|
-
* easy. For anonymous function expressions it is much harder. If you search for
|
|
8184
|
-
* `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
|
|
8185
|
-
* where JS gives anonymous function expressions names. We roughly detect the
|
|
8186
|
-
* same AST nodes with some exceptions to better fit our use case.
|
|
8187
|
-
*/
|
|
8188
6607
|
function getFunctionName(node) {
|
|
8189
6608
|
if (node.type === "ComponentDeclaration" || node.type === "HookDeclaration" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" && node.id) return node.id;
|
|
8190
6609
|
else if (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
@@ -8195,9 +6614,6 @@ function getFunctionName(node) {
|
|
|
8195
6614
|
else return;
|
|
8196
6615
|
} else return;
|
|
8197
6616
|
}
|
|
8198
|
-
/**
|
|
8199
|
-
* Convenience function for peeking the last item in a stack.
|
|
8200
|
-
*/
|
|
8201
6617
|
function last(array) {
|
|
8202
6618
|
return array[array.length - 1];
|
|
8203
6619
|
}
|
|
@@ -8240,11 +6656,6 @@ const IDENTIFIER_VISITOR_KEYS = {
|
|
|
8240
6656
|
[AST_NODE_TYPES.UpdateExpression]: ["argument"],
|
|
8241
6657
|
[AST_NODE_TYPES.YieldExpression]: ["argument"]
|
|
8242
6658
|
};
|
|
8243
|
-
/**
|
|
8244
|
-
* Get all nested identifiers in a expression like node
|
|
8245
|
-
* @param node The node to get the nested identifiers from
|
|
8246
|
-
* @returns All nested identifiers
|
|
8247
|
-
*/
|
|
8248
6659
|
function getNestedIdentifiers(node) {
|
|
8249
6660
|
const identifiers = [];
|
|
8250
6661
|
simpleTraverse(node, {
|
|
@@ -8273,23 +6684,10 @@ function isTerminatingStatement(node) {
|
|
|
8273
6684
|
if (node.type === AST_NODE_TYPES.BlockStatement) return node.body.some(isTerminatingStatement);
|
|
8274
6685
|
return false;
|
|
8275
6686
|
}
|
|
8276
|
-
/**
|
|
8277
|
-
* Get the display name of a setState call reference for reporting.
|
|
8278
|
-
* @param node The setState call node (CallExpression or Identifier)
|
|
8279
|
-
* @param getText The source text getter
|
|
8280
|
-
* @returns The fully qualified name of the call
|
|
8281
|
-
*/
|
|
8282
6687
|
function getCallName(node, getText) {
|
|
8283
6688
|
if (node.type === AST_NODE_TYPES.CallExpression) return Extract.getFullyQualifiedName(node.callee, getText);
|
|
8284
6689
|
return Extract.getFullyQualifiedName(node, getText);
|
|
8285
6690
|
}
|
|
8286
|
-
/**
|
|
8287
|
-
* Get the actual CallExpression node from a setState call reference.
|
|
8288
|
-
* When the node is an Identifier that is the callee of a CallExpression,
|
|
8289
|
-
* returns the parent CallExpression; otherwise returns the node itself.
|
|
8290
|
-
* @param node The setState call node (CallExpression or Identifier)
|
|
8291
|
-
* @returns The actual CallExpression node
|
|
8292
|
-
*/
|
|
8293
6691
|
function getSetStateCallExpression(node) {
|
|
8294
6692
|
return Check.isIdentifier(node) && node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node ? node.parent : node;
|
|
8295
6693
|
}
|
|
@@ -8377,13 +6775,6 @@ function createFactCollector$3(additionalStateHooks, additionalEffectHooks) {
|
|
|
8377
6775
|
|
|
8378
6776
|
//#endregion
|
|
8379
6777
|
//#region src/rules/set-state-in-effect/origins.ts
|
|
8380
|
-
/**
|
|
8381
|
-
* Create a resolver that answers whether an identifier or call originates from
|
|
8382
|
-
* a `useState`-like hook's setter function (the element at index 1 of the
|
|
8383
|
-
* returned tuple).
|
|
8384
|
-
* @param context The rule context
|
|
8385
|
-
* @returns The setState provenance resolver
|
|
8386
|
-
*/
|
|
8387
6778
|
function createSetStateResolver$1(context) {
|
|
8388
6779
|
const { additionalStateHooks } = getSettingsFromContext(context);
|
|
8389
6780
|
function isUseStateCall(node) {
|
|
@@ -8395,7 +6786,7 @@ function createSetStateResolver$1(context) {
|
|
|
8395
6786
|
if (initNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
8396
6787
|
if (!isUseStateCall(initNode)) return false;
|
|
8397
6788
|
const variableNodeParent = initNode.parent;
|
|
8398
|
-
if (!("id"
|
|
6789
|
+
if (!hasProperty(variableNodeParent, "id") || variableNodeParent.id?.type !== AST_NODE_TYPES.ArrayPattern) return true;
|
|
8399
6790
|
return variableNodeParent.id.elements.findIndex((e) => e != null && Check.isIdentifier(e, id.name)) === at;
|
|
8400
6791
|
}
|
|
8401
6792
|
function isSetStateCall(node) {
|
|
@@ -8404,7 +6795,7 @@ function createSetStateResolver$1(context) {
|
|
|
8404
6795
|
case AST_NODE_TYPES.CallExpression: {
|
|
8405
6796
|
const innerCallee = Extract.unwrap(callee.callee);
|
|
8406
6797
|
if (innerCallee.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
8407
|
-
if (!("name"
|
|
6798
|
+
if (!hasProperty(innerCallee.object, "name")) return false;
|
|
8408
6799
|
const isAt = Extract.getCalleeName(callee) === "at";
|
|
8409
6800
|
const [index] = callee.arguments;
|
|
8410
6801
|
if (!isAt || index == null) return false;
|
|
@@ -8413,7 +6804,7 @@ function createSetStateResolver$1(context) {
|
|
|
8413
6804
|
}
|
|
8414
6805
|
case AST_NODE_TYPES.Identifier: return isSetStateId(callee, 1);
|
|
8415
6806
|
case AST_NODE_TYPES.MemberExpression: {
|
|
8416
|
-
if (!("name"
|
|
6807
|
+
if (!hasProperty(callee.object, "name")) return false;
|
|
8417
6808
|
const property = callee.property;
|
|
8418
6809
|
const propertyScope = context.sourceCode.getScope(node);
|
|
8419
6810
|
return getStaticValue(property, propertyScope)?.value === 1 && isSetStateId(callee.object, 1);
|
|
@@ -8461,12 +6852,6 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
|
|
|
8461
6852
|
}
|
|
8462
6853
|
return false;
|
|
8463
6854
|
}
|
|
8464
|
-
/**
|
|
8465
|
-
* Check if the setState call is using a ref value, which is safe to use in an effect (ex: `setState(ref.current.scrollTop)`)
|
|
8466
|
-
* @param context The rule context
|
|
8467
|
-
* @param node The setState call argument to check
|
|
8468
|
-
* @returns `true` if the argument value is derived from a ref
|
|
8469
|
-
*/
|
|
8470
6855
|
function isArgumentUsingRefValue(context, node) {
|
|
8471
6856
|
const isUsingRefValue = (n) => {
|
|
8472
6857
|
switch (n.type) {
|
|
@@ -8492,13 +6877,6 @@ function isArgumentUsingRefValue(context, node) {
|
|
|
8492
6877
|
if (isUsingRefValue(node)) return true;
|
|
8493
6878
|
return Check.isFunction(node) && context.sourceCode.getScope(node.body).references.some((r) => isUsingRefValue(r.identifier));
|
|
8494
6879
|
}
|
|
8495
|
-
/**
|
|
8496
|
-
* Check if a setState call is inside a conditional block whose test expression
|
|
8497
|
-
* is derived from a ref value (e.g. `if (prevRef.current !== value) setState(...)`).
|
|
8498
|
-
* @param context The ESLint rule context
|
|
8499
|
-
* @param node The AST node to check
|
|
8500
|
-
* @returns `true` if the node is inside a ref-gated conditional block
|
|
8501
|
-
*/
|
|
8502
6880
|
function isRefGatedContext(context, node) {
|
|
8503
6881
|
let child = node;
|
|
8504
6882
|
let current = node.parent;
|
|
@@ -8526,11 +6904,6 @@ function isRefGatedContext(context, node) {
|
|
|
8526
6904
|
function isRefInExpression(context, node) {
|
|
8527
6905
|
return getNestedIdentifiers(node).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id)));
|
|
8528
6906
|
}
|
|
8529
|
-
/**
|
|
8530
|
-
* Get the name that identifies the source of a `.current` member access.
|
|
8531
|
-
* @param node The object of the member expression
|
|
8532
|
-
* @returns The identifier name, the last property name of a member chain, or null
|
|
8533
|
-
*/
|
|
8534
6907
|
function getRefSourceName(node) {
|
|
8535
6908
|
switch (node.type) {
|
|
8536
6909
|
case AST_NODE_TYPES.Identifier: return node.name;
|
|
@@ -8544,15 +6917,6 @@ function getRefSourceName(node) {
|
|
|
8544
6917
|
|
|
8545
6918
|
//#endregion
|
|
8546
6919
|
//#region src/rules/set-state-in-effect/effects.ts
|
|
8547
|
-
/**
|
|
8548
|
-
* Infer the setState calls that run synchronously during an effect setup from
|
|
8549
|
-
* the collected facts: calls made directly in the setup body (or an IIFE within
|
|
8550
|
-
* it), and calls reached indirectly through local functions, hook callbacks, or
|
|
8551
|
-
* setup identifiers passed to the effect.
|
|
8552
|
-
* @param context The rule context
|
|
8553
|
-
* @param facts The collected facts
|
|
8554
|
-
* @returns The violations to report, in report order
|
|
8555
|
-
*/
|
|
8556
6920
|
function inferViolations$2(context, facts) {
|
|
8557
6921
|
const { additionalEffectHooks } = getSettingsFromContext(context);
|
|
8558
6922
|
const resolver = createSetStateResolver$1(context);
|
|
@@ -8773,13 +7137,6 @@ function createFactCollector$2(additionalStateHooks) {
|
|
|
8773
7137
|
|
|
8774
7138
|
//#endregion
|
|
8775
7139
|
//#region src/rules/set-state-in-render/origins.ts
|
|
8776
|
-
/**
|
|
8777
|
-
* Create a resolver that answers whether an identifier or call originates from
|
|
8778
|
-
* a `useState`-like hook's setter function (the element at index 1 of the
|
|
8779
|
-
* returned tuple).
|
|
8780
|
-
* @param context The rule context
|
|
8781
|
-
* @returns The setState provenance resolver
|
|
8782
|
-
*/
|
|
8783
7140
|
function createSetStateResolver(context) {
|
|
8784
7141
|
const { additionalStateHooks } = getSettingsFromContext(context);
|
|
8785
7142
|
function isUseStateCall(node) {
|
|
@@ -8791,7 +7148,7 @@ function createSetStateResolver(context) {
|
|
|
8791
7148
|
if (initNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
8792
7149
|
if (!isUseStateCall(initNode)) return false;
|
|
8793
7150
|
const variableNodeParent = initNode.parent;
|
|
8794
|
-
if (!("id"
|
|
7151
|
+
if (!hasProperty(variableNodeParent, "id") || variableNodeParent.id?.type !== AST_NODE_TYPES.ArrayPattern) return true;
|
|
8795
7152
|
return variableNodeParent.id.elements.findIndex((e) => e != null && Check.isIdentifier(e, id.name)) === at;
|
|
8796
7153
|
}
|
|
8797
7154
|
function isSetStateCall(node) {
|
|
@@ -8800,7 +7157,7 @@ function createSetStateResolver(context) {
|
|
|
8800
7157
|
case AST_NODE_TYPES.CallExpression: {
|
|
8801
7158
|
const innerCallee = Extract.unwrap(callee.callee);
|
|
8802
7159
|
if (innerCallee.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
8803
|
-
if (!("name"
|
|
7160
|
+
if (!hasProperty(innerCallee.object, "name")) return false;
|
|
8804
7161
|
const isAt = Extract.getCalleeName(callee) === "at";
|
|
8805
7162
|
const [index] = callee.arguments;
|
|
8806
7163
|
if (!isAt || index == null) return false;
|
|
@@ -8809,7 +7166,7 @@ function createSetStateResolver(context) {
|
|
|
8809
7166
|
}
|
|
8810
7167
|
case AST_NODE_TYPES.Identifier: return isSetStateId(callee, 1);
|
|
8811
7168
|
case AST_NODE_TYPES.MemberExpression: {
|
|
8812
|
-
if (!("name"
|
|
7169
|
+
if (!hasProperty(callee.object, "name")) return false;
|
|
8813
7170
|
const property = callee.property;
|
|
8814
7171
|
const propertyScope = context.sourceCode.getScope(node);
|
|
8815
7172
|
return getStaticValue(property, propertyScope)?.value === 1 && isSetStateId(callee.object, 1);
|
|
@@ -8826,14 +7183,6 @@ function createSetStateResolver(context) {
|
|
|
8826
7183
|
|
|
8827
7184
|
//#endregion
|
|
8828
7185
|
//#region src/rules/set-state-in-render/effects.ts
|
|
8829
|
-
/**
|
|
8830
|
-
* Infer the setState calls that run unconditionally during render from the
|
|
8831
|
-
* collected facts: calls made directly in a component function body that are
|
|
8832
|
-
* neither guarded by a conditional nor positioned after an early return.
|
|
8833
|
-
* @param context The rule context
|
|
8834
|
-
* @param facts The collected facts
|
|
8835
|
-
* @returns The violations to report, in report order
|
|
8836
|
-
*/
|
|
8837
7186
|
function inferViolations$1(context, facts) {
|
|
8838
7187
|
const resolver = createSetStateResolver(context);
|
|
8839
7188
|
const getText = (n) => context.sourceCode.getText(n);
|
|
@@ -8900,10 +7249,6 @@ function createFactCollector$1() {
|
|
|
8900
7249
|
|
|
8901
7250
|
//#endregion
|
|
8902
7251
|
//#region src/rules/static-components/helpers.ts
|
|
8903
|
-
/**
|
|
8904
|
-
* Expression kinds that always produce a brand-new value on every evaluation, and are
|
|
8905
|
-
* therefore considered "dynamically created" when used as a component's value.
|
|
8906
|
-
*/
|
|
8907
7252
|
const KNOWN_DYNAMIC_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
8908
7253
|
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
8909
7254
|
AST_NODE_TYPES.CallExpression,
|
|
@@ -8911,10 +7256,6 @@ const KNOWN_DYNAMIC_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
|
8911
7256
|
AST_NODE_TYPES.FunctionExpression,
|
|
8912
7257
|
AST_NODE_TYPES.NewExpression
|
|
8913
7258
|
]);
|
|
8914
|
-
/**
|
|
8915
|
-
* Builds a predicate that tells whether a given node lies within the body of one of the
|
|
8916
|
-
* collected function or class components, i.e. whether it is "created during render".
|
|
8917
|
-
*/
|
|
8918
7259
|
function createRenderBoundaryChecker(componentNodes) {
|
|
8919
7260
|
const components = new Set(componentNodes);
|
|
8920
7261
|
return (node) => Traverse.findParent(node, (n) => (Check.isFunction(n) || Check.isClass(n)) && components.has(n)) != null;
|
|
@@ -8922,11 +7263,6 @@ function createRenderBoundaryChecker(componentNodes) {
|
|
|
8922
7263
|
|
|
8923
7264
|
//#endregion
|
|
8924
7265
|
//#region src/rules/static-components/origins.ts
|
|
8925
|
-
/**
|
|
8926
|
-
* Resolves an expression down to the node that dynamically created its value, if any.
|
|
8927
|
-
* Recurses through identifier references, both branches of ternaries and logical
|
|
8928
|
-
* expressions, and the final element of sequence expressions.
|
|
8929
|
-
*/
|
|
8930
7266
|
function findDynamicCreationSite(context, node, isInsideRender, seen) {
|
|
8931
7267
|
const expr = Extract.unwrap(node);
|
|
8932
7268
|
if (KNOWN_DYNAMIC_EXPRESSION_TYPES.has(expr.type)) return expr;
|
|
@@ -8946,10 +7282,6 @@ function findDynamicCreationSite(context, node, isInsideRender, seen) {
|
|
|
8946
7282
|
default: return null;
|
|
8947
7283
|
}
|
|
8948
7284
|
}
|
|
8949
|
-
/**
|
|
8950
|
-
* Looks for a reassignment of `variable` (e.g. `Component = createComponent();`) whose
|
|
8951
|
-
* right-hand side is dynamically created.
|
|
8952
|
-
*/
|
|
8953
7285
|
function findReassignmentCreationSite(context, variable, isInsideRender, seen) {
|
|
8954
7286
|
for (const ref of variable.references) {
|
|
8955
7287
|
if (!ref.isWrite()) continue;
|
|
@@ -8960,10 +7292,6 @@ function findReassignmentCreationSite(context, variable, isInsideRender, seen) {
|
|
|
8960
7292
|
}
|
|
8961
7293
|
return null;
|
|
8962
7294
|
}
|
|
8963
|
-
/**
|
|
8964
|
-
* Resolves a component binding to the node that dynamically created its value, if the
|
|
8965
|
-
* value is created during render (a fresh function/class/call result on every render).
|
|
8966
|
-
*/
|
|
8967
7295
|
function resolveDynamicComponentOrigin(context, variable, isInsideRender, seen = /* @__PURE__ */ new Set()) {
|
|
8968
7296
|
if (seen.has(variable)) return {
|
|
8969
7297
|
creationNode: null,
|
|
@@ -8997,11 +7325,6 @@ function resolveDynamicComponentOrigin(context, variable, isInsideRender, seen =
|
|
|
8997
7325
|
|
|
8998
7326
|
//#endregion
|
|
8999
7327
|
//#region src/rules/static-components/effects.ts
|
|
9000
|
-
/**
|
|
9001
|
-
* Turn collected JSX component usages into effects: a usage is an effect when the
|
|
9002
|
-
* binding it refers to is declared inside a component's render body and its value is
|
|
9003
|
-
* dynamically created, i.e. the component is recreated on every render.
|
|
9004
|
-
*/
|
|
9005
7328
|
function inferCreatedComponents(context, componentUsages, isInsideRender) {
|
|
9006
7329
|
const effects = [];
|
|
9007
7330
|
for (const usage of componentUsages) {
|
|
@@ -9142,22 +7465,11 @@ function create$2(context) {
|
|
|
9142
7465
|
|
|
9143
7466
|
//#endregion
|
|
9144
7467
|
//#region src/rules/use-memo/helpers.ts
|
|
9145
|
-
/**
|
|
9146
|
-
* Check whether the result of a `useMemo` call is used (assigned, returned,
|
|
9147
|
-
* passed as an argument, etc.) rather than discarded as a bare statement.
|
|
9148
|
-
* @param node The `useMemo` call expression
|
|
9149
|
-
* @returns `true` when the call result flows into a surrounding expression
|
|
9150
|
-
*/
|
|
9151
7468
|
function isResultUsed(node) {
|
|
9152
7469
|
let parent = node.parent;
|
|
9153
7470
|
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
9154
7471
|
return parent.type === AST_NODE_TYPES.VariableDeclarator || parent.type === AST_NODE_TYPES.AssignmentExpression || parent.type === AST_NODE_TYPES.AssignmentPattern || parent.type === AST_NODE_TYPES.Property || parent.type === AST_NODE_TYPES.ReturnStatement || parent.type === AST_NODE_TYPES.JSXExpressionContainer || parent.type === AST_NODE_TYPES.CallExpression || parent.type === AST_NODE_TYPES.NewExpression || parent.type === AST_NODE_TYPES.ArrayExpression || parent.type === AST_NODE_TYPES.ConditionalExpression || parent.type === AST_NODE_TYPES.LogicalExpression || parent.type === AST_NODE_TYPES.SequenceExpression || parent.type === AST_NODE_TYPES.SpreadElement || parent.type === AST_NODE_TYPES.TemplateLiteral || parent.type === AST_NODE_TYPES.BinaryExpression || parent.type === AST_NODE_TYPES.UnaryExpression || parent.type === AST_NODE_TYPES.MemberExpression || parent.type === AST_NODE_TYPES.TaggedTemplateExpression || parent.type === AST_NODE_TYPES.ChainExpression || parent.type === AST_NODE_TYPES.ArrowFunctionExpression || parent.type === AST_NODE_TYPES.ForOfStatement || parent.type === AST_NODE_TYPES.ForInStatement;
|
|
9155
7472
|
}
|
|
9156
|
-
/**
|
|
9157
|
-
* Gets the nested return statements in the node that are within the same function
|
|
9158
|
-
* @param node The AST node
|
|
9159
|
-
* @returns The nested return statements in the node
|
|
9160
|
-
*/
|
|
9161
7473
|
function getNestedReturnStatements(node) {
|
|
9162
7474
|
const statements = [];
|
|
9163
7475
|
const boundary = Check.isFunction(node) ? node : Traverse.findParent(node, Check.isFunction);
|
|
@@ -9226,15 +7538,6 @@ function isDeclaredInsideCallback(variable, callback) {
|
|
|
9226
7538
|
}
|
|
9227
7539
|
return false;
|
|
9228
7540
|
}
|
|
9229
|
-
/**
|
|
9230
|
-
* Resolve whether an identifier written inside a `useMemo` callback refers to a
|
|
9231
|
-
* variable declared outside the callback. Unresolved identifiers and variables
|
|
9232
|
-
* without definitions are treated as outer variables.
|
|
9233
|
-
* @param context The rule context
|
|
9234
|
-
* @param target The written identifier
|
|
9235
|
-
* @param callback The `useMemo` callback the write occurs in
|
|
9236
|
-
* @returns `true` when the write reassigns an outer variable
|
|
9237
|
-
*/
|
|
9238
7541
|
function isOuterVariable(context, target, callback) {
|
|
9239
7542
|
const scope = context.sourceCode.getScope(target);
|
|
9240
7543
|
const variable = findVariable(scope, target);
|
|
@@ -9244,14 +7547,6 @@ function isOuterVariable(context, target, callback) {
|
|
|
9244
7547
|
|
|
9245
7548
|
//#endregion
|
|
9246
7549
|
//#region src/rules/use-memo/effects.ts
|
|
9247
|
-
/**
|
|
9248
|
-
* Infer the `useMemo` usage violations from the collected facts: unused
|
|
9249
|
-
* results, callbacks with parameters, async or generator callbacks, outer
|
|
9250
|
-
* variable reassignments, and callbacks that never return a value.
|
|
9251
|
-
* @param context The rule context
|
|
9252
|
-
* @param facts The collected facts
|
|
9253
|
-
* @returns The violations to report, in report order
|
|
9254
|
-
*/
|
|
9255
7550
|
function inferViolations(context, facts) {
|
|
9256
7551
|
const violations = [];
|
|
9257
7552
|
for (const fact of facts.calls) {
|
|
@@ -9384,12 +7679,6 @@ const EXPRESSION_VISITOR_KEYS = {
|
|
|
9384
7679
|
[AST_NODE_TYPES.WhileStatement]: ["test"],
|
|
9385
7680
|
[AST_NODE_TYPES.YieldExpression]: ["argument"]
|
|
9386
7681
|
};
|
|
9387
|
-
/**
|
|
9388
|
-
* Get all nested expressions of type T in an expression like node
|
|
9389
|
-
* @param type The type of the expression to retrieve within the node
|
|
9390
|
-
* @returns A partially applied function bound to a predicate of type AST. The returned function can be called passing a
|
|
9391
|
-
* node, and it will return an array of all nested expressions of type AST.
|
|
9392
|
-
*/
|
|
9393
7682
|
function getNestedExpressionsOfType(type) {
|
|
9394
7683
|
const isNodeOfType = Check.is(type);
|
|
9395
7684
|
const recurse = (node) => {
|
|
@@ -9405,17 +7694,7 @@ function getNestedExpressionsOfType(type) {
|
|
|
9405
7694
|
};
|
|
9406
7695
|
return recurse;
|
|
9407
7696
|
}
|
|
9408
|
-
/**
|
|
9409
|
-
* Get all nested new expressions in an expression like node
|
|
9410
|
-
* @param node The node to get the nested new expressions from
|
|
9411
|
-
* @returns All nested new expressions
|
|
9412
|
-
*/
|
|
9413
7697
|
const getNestedNewExpressions = getNestedExpressionsOfType(AST_NODE_TYPES.NewExpression);
|
|
9414
|
-
/**
|
|
9415
|
-
* Get all nested call expressions in a expression like node
|
|
9416
|
-
* @param node The node to get the nested call expressions from
|
|
9417
|
-
* @returns All nested call expressions
|
|
9418
|
-
*/
|
|
9419
7698
|
const getNestedCallExpressions = getNestedExpressionsOfType(AST_NODE_TYPES.CallExpression);
|
|
9420
7699
|
|
|
9421
7700
|
//#endregion
|
|
@@ -9468,7 +7747,7 @@ function create(context) {
|
|
|
9468
7747
|
const [useStateInput] = node.arguments;
|
|
9469
7748
|
if (useStateInput != null) {
|
|
9470
7749
|
for (const expr of getNestedNewExpressions(useStateInput)) {
|
|
9471
|
-
if (!("name"
|
|
7750
|
+
if (!hasProperty(expr.callee, "name")) continue;
|
|
9472
7751
|
if (LAZY_INIT_ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
9473
7752
|
if (Traverse.findParent(expr, (n) => core.isUseCall(context, n)) != null) continue;
|
|
9474
7753
|
context.report({
|
|
@@ -9477,7 +7756,7 @@ function create(context) {
|
|
|
9477
7756
|
});
|
|
9478
7757
|
}
|
|
9479
7758
|
for (const expr of getNestedCallExpressions(useStateInput)) {
|
|
9480
|
-
if (!("name"
|
|
7759
|
+
if (!hasProperty(expr.callee, "name")) continue;
|
|
9481
7760
|
if (core.isHookName(expr.callee.name)) continue;
|
|
9482
7761
|
if (LAZY_INIT_ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
9483
7762
|
if (Traverse.findParent(expr, (n) => core.isUseCall(context, n)) != null) continue;
|
|
@@ -9749,45 +8028,15 @@ function createConfig(base) {
|
|
|
9749
8028
|
const finalPlugin = {
|
|
9750
8029
|
...plugin,
|
|
9751
8030
|
configs: {
|
|
9752
|
-
/**
|
|
9753
|
-
* Disable rules in `eslint-plugin-react` that conflict with rules in this plugin
|
|
9754
|
-
*/
|
|
9755
8031
|
["disable-conflict-eslint-plugin-react"]: createConfig(disable_conflict_eslint_plugin_react_exports),
|
|
9756
|
-
/**
|
|
9757
|
-
* Disable rules in `eslint-plugin-react-hooks` that conflict with rules in this plugin
|
|
9758
|
-
*/
|
|
9759
8032
|
["disable-conflict-eslint-plugin-react-hooks"]: createConfig(disable_conflict_eslint_plugin_react_hooks_exports),
|
|
9760
|
-
/**
|
|
9761
|
-
* Disable experimental rules that might be subject to change in the future
|
|
9762
|
-
*/
|
|
9763
8033
|
["disable-experimental"]: createConfig(disable_experimental_exports),
|
|
9764
|
-
/**
|
|
9765
|
-
* Disable rules that can be enforced by TypeScript
|
|
9766
|
-
*/
|
|
9767
8034
|
["disable-type-checked"]: createConfig(disable_type_checked_exports),
|
|
9768
|
-
/**
|
|
9769
|
-
* Enforce rules that are recommended by ESLint React for general purpose React + React DOM projects
|
|
9770
|
-
*/
|
|
9771
8035
|
["recommended"]: createConfig(recommended_exports),
|
|
9772
|
-
/**
|
|
9773
|
-
* Same as the `recommended-typescript` preset but enables additional rules that require type information
|
|
9774
|
-
*/
|
|
9775
8036
|
["recommended-type-checked"]: createConfig(recommended_type_checked_exports),
|
|
9776
|
-
/**
|
|
9777
|
-
* Same as the `recommended` preset but disables rules that can be enforced by TypeScript
|
|
9778
|
-
*/
|
|
9779
8037
|
["recommended-typescript"]: createConfig(recommended_typescript_exports),
|
|
9780
|
-
/**
|
|
9781
|
-
* More strict version of the `recommended` preset
|
|
9782
|
-
*/
|
|
9783
8038
|
["strict"]: createConfig(strict_exports),
|
|
9784
|
-
/**
|
|
9785
|
-
* Same as the `strict-typescript` preset but enables additional rules that require type information
|
|
9786
|
-
*/
|
|
9787
8039
|
["strict-type-checked"]: createConfig(strict_type_checked_exports),
|
|
9788
|
-
/**
|
|
9789
|
-
* Same as the `strict` preset but disables rules that can be enforced by TypeScript
|
|
9790
|
-
*/
|
|
9791
8040
|
["strict-typescript"]: createConfig(strict_typescript_exports)
|
|
9792
8041
|
}
|
|
9793
8042
|
};
|