eslint-plugin-react-x 5.24.0 → 5.24.2
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 +45 -1476
- package/package.json +8 -8
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.2";
|
|
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,60 +1982,6 @@ function create$41(context) {
|
|
|
2094
1982
|
|
|
2095
1983
|
//#endregion
|
|
2096
1984
|
//#region ../../.pkgs/eff/dist/index.js
|
|
2097
|
-
function not(predicate) {
|
|
2098
|
-
return (data) => !predicate(data);
|
|
2099
|
-
}
|
|
2100
|
-
function or(a, b) {
|
|
2101
|
-
return (data) => a(data) || b(data);
|
|
2102
|
-
}
|
|
2103
|
-
/**
|
|
2104
|
-
* A function that checks if the passed parameter is a string and narrows its type accordingly.
|
|
2105
|
-
*
|
|
2106
|
-
* @param data - The variable to check.
|
|
2107
|
-
* @returns True if the passed input is a string, false otherwise.
|
|
2108
|
-
* @category guards
|
|
2109
|
-
*/
|
|
2110
|
-
function isString(data) {
|
|
2111
|
-
return typeof data === "string";
|
|
2112
|
-
}
|
|
2113
|
-
/**
|
|
2114
|
-
* Applies a `pipe` method's variadic arguments to an initial value from left
|
|
2115
|
-
* to right.
|
|
2116
|
-
*
|
|
2117
|
-
* **When to use**
|
|
2118
|
-
*
|
|
2119
|
-
* Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
|
|
2120
|
-
* object.
|
|
2121
|
-
*
|
|
2122
|
-
* **Details**
|
|
2123
|
-
*
|
|
2124
|
-
* This helper is intended for implementing `Pipeable.pipe` methods that
|
|
2125
|
-
* receive JavaScript's `arguments` object. With no functions it returns the
|
|
2126
|
-
* original value; otherwise it feeds each result into the next function.
|
|
2127
|
-
*
|
|
2128
|
-
* **Example** (Implementing a pipe method)
|
|
2129
|
-
*
|
|
2130
|
-
* ```ts
|
|
2131
|
-
* import { Pipeable } from "effect"
|
|
2132
|
-
*
|
|
2133
|
-
* class NumberBox {
|
|
2134
|
-
* constructor(readonly value: number) {}
|
|
2135
|
-
*
|
|
2136
|
-
* pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
|
|
2137
|
-
* return Pipeable.pipeArguments(this.value, arguments) as number
|
|
2138
|
-
* }
|
|
2139
|
-
* }
|
|
2140
|
-
*
|
|
2141
|
-
* const result = new NumberBox(5).pipe(
|
|
2142
|
-
* (n) => n + 2,
|
|
2143
|
-
* (n) => n * 3
|
|
2144
|
-
* )
|
|
2145
|
-
* console.log(result) // 21
|
|
2146
|
-
* ```
|
|
2147
|
-
*
|
|
2148
|
-
* @category combinators
|
|
2149
|
-
* @since 2.0.0
|
|
2150
|
-
*/
|
|
2151
1985
|
const pipeArguments = (self, args) => {
|
|
2152
1986
|
switch (args.length) {
|
|
2153
1987
|
case 0: return self;
|
|
@@ -2167,110 +2001,14 @@ const pipeArguments = (self, args) => {
|
|
|
2167
2001
|
}
|
|
2168
2002
|
}
|
|
2169
2003
|
};
|
|
2170
|
-
/**
|
|
2171
|
-
* Reusable prototype that implements `Pipeable.pipe`.
|
|
2172
|
-
*
|
|
2173
|
-
* **When to use**
|
|
2174
|
-
*
|
|
2175
|
-
* Use when classes or object prototypes can reuse this value when they need the
|
|
2176
|
-
* standard pipe implementation backed by `pipeArguments`.
|
|
2177
|
-
*
|
|
2178
|
-
* @category prototypes
|
|
2179
|
-
* @since 3.15.0
|
|
2180
|
-
*/
|
|
2181
2004
|
const Prototype = { pipe() {
|
|
2182
2005
|
return pipeArguments(this, arguments);
|
|
2183
2006
|
} };
|
|
2184
|
-
/**
|
|
2185
|
-
* Provides a base constructor whose instances implement the standard `Pipeable.pipe`
|
|
2186
|
-
* method.
|
|
2187
|
-
*
|
|
2188
|
-
* **When to use**
|
|
2189
|
-
*
|
|
2190
|
-
* Use when you need to define a class that supports Effect-style method
|
|
2191
|
-
* chaining through `.pipe(...)`.
|
|
2192
|
-
*
|
|
2193
|
-
* @category constructors
|
|
2194
|
-
* @since 3.15.0
|
|
2195
|
-
*/
|
|
2196
2007
|
const Class = (function() {
|
|
2197
2008
|
function PipeableBase() {}
|
|
2198
2009
|
PipeableBase.prototype = Prototype;
|
|
2199
2010
|
return PipeableBase;
|
|
2200
2011
|
})();
|
|
2201
|
-
/**
|
|
2202
|
-
* Provides small helpers for defining and reusing TypeScript functions.
|
|
2203
|
-
*
|
|
2204
|
-
* The main helpers are `pipe` and `flow` for left-to-right composition and
|
|
2205
|
-
* `dual` for APIs that support both direct and pipe-friendly call styles. The
|
|
2206
|
-
* module also contains small identity, constant, tuple, type-level, and
|
|
2207
|
-
* memoization helpers used across the library.
|
|
2208
|
-
*
|
|
2209
|
-
* @since 2.0.0
|
|
2210
|
-
*/
|
|
2211
|
-
/**
|
|
2212
|
-
* Creates a function that can be called in data-first style or data-last
|
|
2213
|
-
* (`pipe`-friendly) style.
|
|
2214
|
-
*
|
|
2215
|
-
* **When to use**
|
|
2216
|
-
*
|
|
2217
|
-
* Use to expose one implementation through both direct and `pipe`-friendly
|
|
2218
|
-
* call styles.
|
|
2219
|
-
*
|
|
2220
|
-
* **Details**
|
|
2221
|
-
*
|
|
2222
|
-
* Pass either the arity of the uncurried function or a predicate that decides
|
|
2223
|
-
* whether the current call is data-first. Arity is the common case. Use a
|
|
2224
|
-
* predicate when optional arguments make arity ambiguous.
|
|
2225
|
-
*
|
|
2226
|
-
* **Example** (Selecting data-first or data-last style by arity)
|
|
2227
|
-
*
|
|
2228
|
-
* ```ts
|
|
2229
|
-
* import { Function, pipe } from "effect"
|
|
2230
|
-
*
|
|
2231
|
-
* const sum = Function.dual<
|
|
2232
|
-
* (that: number) => (self: number) => number,
|
|
2233
|
-
* (self: number, that: number) => number
|
|
2234
|
-
* >(2, (self, that) => self + that)
|
|
2235
|
-
*
|
|
2236
|
-
* console.log(sum(2, 3)) // 5
|
|
2237
|
-
* console.log(pipe(2, sum(3))) // 5
|
|
2238
|
-
* ```
|
|
2239
|
-
*
|
|
2240
|
-
* **Example** (Defining overloads with call signatures)
|
|
2241
|
-
*
|
|
2242
|
-
* ```ts
|
|
2243
|
-
* import { Function, pipe } from "effect"
|
|
2244
|
-
*
|
|
2245
|
-
* const sum: {
|
|
2246
|
-
* (that: number): (self: number) => number
|
|
2247
|
-
* (self: number, that: number): number
|
|
2248
|
-
* } = Function.dual(2, (self: number, that: number): number => self + that)
|
|
2249
|
-
*
|
|
2250
|
-
* console.log(sum(2, 3)) // 5
|
|
2251
|
-
* console.log(pipe(2, sum(3))) // 5
|
|
2252
|
-
* ```
|
|
2253
|
-
*
|
|
2254
|
-
* **Example** (Selecting data-first or data-last style with a predicate)
|
|
2255
|
-
*
|
|
2256
|
-
* ```ts
|
|
2257
|
-
* import { Function, pipe } from "effect"
|
|
2258
|
-
*
|
|
2259
|
-
* const sum = Function.dual<
|
|
2260
|
-
* (that: number) => (self: number) => number,
|
|
2261
|
-
* (self: number, that: number) => number
|
|
2262
|
-
* >(
|
|
2263
|
-
* (args) => args.length === 2,
|
|
2264
|
-
* (self, that) => self + that
|
|
2265
|
-
* )
|
|
2266
|
-
*
|
|
2267
|
-
* console.log(sum(2, 3)) // 5
|
|
2268
|
-
* console.log(pipe(2, sum(3))) // 5
|
|
2269
|
-
* ```
|
|
2270
|
-
*
|
|
2271
|
-
* @category combinators
|
|
2272
|
-
* @since 2.0.0
|
|
2273
|
-
*/
|
|
2274
2012
|
const dual = function(arity, body) {
|
|
2275
2013
|
if (typeof arity === "function") return function() {
|
|
2276
2014
|
return arity(arguments) ? body.apply(this, arguments) : ((self) => body(self, ...arguments));
|
|
@@ -2299,203 +2037,14 @@ const dual = function(arity, body) {
|
|
|
2299
2037
|
};
|
|
2300
2038
|
}
|
|
2301
2039
|
};
|
|
2302
|
-
/**
|
|
2303
|
-
* Returns its input argument unchanged.
|
|
2304
|
-
*
|
|
2305
|
-
* **When to use**
|
|
2306
|
-
*
|
|
2307
|
-
* Use to return a value unchanged where a function is required.
|
|
2308
|
-
*
|
|
2309
|
-
* **Example** (Returning the same value)
|
|
2310
|
-
*
|
|
2311
|
-
* ```ts
|
|
2312
|
-
* import { identity } from "effect"
|
|
2313
|
-
* import * as assert from "node:assert"
|
|
2314
|
-
*
|
|
2315
|
-
* assert.deepStrictEqual(identity(5), 5)
|
|
2316
|
-
* ```
|
|
2317
|
-
*
|
|
2318
|
-
* @category combinators
|
|
2319
|
-
* @since 2.0.0
|
|
2320
|
-
*/
|
|
2321
2040
|
const identity = (a) => a;
|
|
2322
|
-
/**
|
|
2323
|
-
* Returns the input value with a different static type.
|
|
2324
|
-
*
|
|
2325
|
-
* **When to use**
|
|
2326
|
-
*
|
|
2327
|
-
* Use when you need an explicit type-level cast and accept that the value is
|
|
2328
|
-
* returned unchanged at runtime.
|
|
2329
|
-
*
|
|
2330
|
-
* **Gotchas**
|
|
2331
|
-
*
|
|
2332
|
-
* This is a type-level cast only; it performs no runtime validation or
|
|
2333
|
-
* conversion.
|
|
2334
|
-
*
|
|
2335
|
-
* @see {@link satisfies} for checking assignability without changing the resulting type
|
|
2336
|
-
*
|
|
2337
|
-
* @category utility types
|
|
2338
|
-
* @since 4.0.0
|
|
2339
|
-
*/
|
|
2340
2041
|
const cast = identity;
|
|
2341
|
-
/**
|
|
2342
|
-
* Creates a zero-argument function that always returns the provided value.
|
|
2343
|
-
*
|
|
2344
|
-
* **When to use**
|
|
2345
|
-
*
|
|
2346
|
-
* Use when you need a thunk or callback that returns the same value on every
|
|
2347
|
-
* invocation.
|
|
2348
|
-
*
|
|
2349
|
-
* **Example** (Creating a constant thunk)
|
|
2350
|
-
*
|
|
2351
|
-
* ```ts
|
|
2352
|
-
* import { Function } from "effect"
|
|
2353
|
-
* import * as assert from "node:assert"
|
|
2354
|
-
*
|
|
2355
|
-
* const constNull = Function.constant(null)
|
|
2356
|
-
*
|
|
2357
|
-
* assert.deepStrictEqual(constNull(), null)
|
|
2358
|
-
* assert.deepStrictEqual(constNull(), null)
|
|
2359
|
-
* ```
|
|
2360
|
-
*
|
|
2361
|
-
* @category constructors
|
|
2362
|
-
* @since 2.0.0
|
|
2363
|
-
*/
|
|
2364
2042
|
const constant = (value) => () => value;
|
|
2365
|
-
/**
|
|
2366
|
-
* Returns `true` when called.
|
|
2367
|
-
*
|
|
2368
|
-
* **When to use**
|
|
2369
|
-
*
|
|
2370
|
-
* Use when you need a thunk that returns `true` on every invocation.
|
|
2371
|
-
*
|
|
2372
|
-
* **Example** (Returning true from a thunk)
|
|
2373
|
-
*
|
|
2374
|
-
* ```ts
|
|
2375
|
-
* import { Function } from "effect"
|
|
2376
|
-
* import * as assert from "node:assert"
|
|
2377
|
-
*
|
|
2378
|
-
* assert.deepStrictEqual(Function.constTrue(), true)
|
|
2379
|
-
* ```
|
|
2380
|
-
*
|
|
2381
|
-
* @category constants
|
|
2382
|
-
* @since 2.0.0
|
|
2383
|
-
*/
|
|
2384
2043
|
const constTrue = constant(true);
|
|
2385
|
-
/**
|
|
2386
|
-
* Returns `false` when called.
|
|
2387
|
-
*
|
|
2388
|
-
* **When to use**
|
|
2389
|
-
*
|
|
2390
|
-
* Use when you need a thunk that returns `false` on every invocation.
|
|
2391
|
-
*
|
|
2392
|
-
* **Example** (Returning false from a thunk)
|
|
2393
|
-
*
|
|
2394
|
-
* ```ts
|
|
2395
|
-
* import { Function } from "effect"
|
|
2396
|
-
* import * as assert from "node:assert"
|
|
2397
|
-
*
|
|
2398
|
-
* assert.deepStrictEqual(Function.constFalse(), false)
|
|
2399
|
-
* ```
|
|
2400
|
-
*
|
|
2401
|
-
* @category constants
|
|
2402
|
-
* @since 2.0.0
|
|
2403
|
-
*/
|
|
2404
2044
|
const constFalse = constant(false);
|
|
2405
|
-
/**
|
|
2406
|
-
* Returns `null` when called.
|
|
2407
|
-
*
|
|
2408
|
-
* **When to use**
|
|
2409
|
-
*
|
|
2410
|
-
* Use when you need a thunk that returns `null` on every invocation.
|
|
2411
|
-
*
|
|
2412
|
-
* **Example** (Returning null from a thunk)
|
|
2413
|
-
*
|
|
2414
|
-
* ```ts
|
|
2415
|
-
* import { Function } from "effect"
|
|
2416
|
-
* import * as assert from "node:assert"
|
|
2417
|
-
*
|
|
2418
|
-
* assert.deepStrictEqual(Function.constNull(), null)
|
|
2419
|
-
* ```
|
|
2420
|
-
*
|
|
2421
|
-
* @category constants
|
|
2422
|
-
* @since 2.0.0
|
|
2423
|
-
*/
|
|
2424
2045
|
const constNull = constant(null);
|
|
2425
|
-
/**
|
|
2426
|
-
* Returns `undefined` when called.
|
|
2427
|
-
*
|
|
2428
|
-
* **When to use**
|
|
2429
|
-
*
|
|
2430
|
-
* Use when you need a thunk that returns `undefined` on every invocation.
|
|
2431
|
-
*
|
|
2432
|
-
* **Example** (Returning undefined from a thunk)
|
|
2433
|
-
*
|
|
2434
|
-
* ```ts
|
|
2435
|
-
* import { Function } from "effect"
|
|
2436
|
-
* import * as assert from "node:assert"
|
|
2437
|
-
*
|
|
2438
|
-
* assert.deepStrictEqual(Function.constUndefined(), undefined)
|
|
2439
|
-
* ```
|
|
2440
|
-
*
|
|
2441
|
-
* @category constants
|
|
2442
|
-
* @since 2.0.0
|
|
2443
|
-
*/
|
|
2444
2046
|
const constUndefined = constant(void 0);
|
|
2445
|
-
/**
|
|
2446
|
-
* 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`.
|
|
2447
|
-
* The result is obtained by first applying the `ab` function to `a` and then applying the `bc` function to the result of `ab`.
|
|
2448
|
-
*
|
|
2449
|
-
* **When to use**
|
|
2450
|
-
*
|
|
2451
|
-
* Use to compose exactly two unary functions into a reusable unary function.
|
|
2452
|
-
*
|
|
2453
|
-
* **Example** (Composing two functions)
|
|
2454
|
-
*
|
|
2455
|
-
* ```ts
|
|
2456
|
-
* import { Function } from "effect"
|
|
2457
|
-
* import * as assert from "node:assert"
|
|
2458
|
-
*
|
|
2459
|
-
* const increment = (n: number) => n + 1
|
|
2460
|
-
* const square = (n: number) => n * n
|
|
2461
|
-
*
|
|
2462
|
-
* assert.strictEqual(Function.compose(increment, square)(2), 9)
|
|
2463
|
-
* ```
|
|
2464
|
-
*
|
|
2465
|
-
* @see {@link flow} for composing a left-to-right sequence of functions
|
|
2466
|
-
* @see {@link pipe} for applying a value through a left-to-right sequence immediately
|
|
2467
|
-
*
|
|
2468
|
-
* @category combinators
|
|
2469
|
-
* @since 2.0.0
|
|
2470
|
-
*/
|
|
2471
2047
|
const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
|
|
2472
|
-
/**
|
|
2473
|
-
* Marks an impossible branch by accepting a `never` value and returning any
|
|
2474
|
-
* type.
|
|
2475
|
-
*
|
|
2476
|
-
* **When to use**
|
|
2477
|
-
*
|
|
2478
|
-
* Use when you need a return value in a branch that exhaustive checks prove
|
|
2479
|
-
* cannot be reached.
|
|
2480
|
-
*
|
|
2481
|
-
* **Gotchas**
|
|
2482
|
-
*
|
|
2483
|
-
* Calling `absurd` throws, because a value of type `never` should be
|
|
2484
|
-
* impossible at runtime.
|
|
2485
|
-
*
|
|
2486
|
-
* **Example** (Handling impossible values)
|
|
2487
|
-
*
|
|
2488
|
-
* ```ts
|
|
2489
|
-
* import { absurd } from "effect"
|
|
2490
|
-
*
|
|
2491
|
-
* const handleNever = (value: never) => {
|
|
2492
|
-
* return absurd(value) // This will throw an error if called
|
|
2493
|
-
* }
|
|
2494
|
-
* ```
|
|
2495
|
-
*
|
|
2496
|
-
* @category utility types
|
|
2497
|
-
* @since 2.0.0
|
|
2498
|
-
*/
|
|
2499
2048
|
const absurd = (_) => {
|
|
2500
2049
|
throw new Error("Called `absurd` function which should be uncallable");
|
|
2501
2050
|
};
|
|
@@ -2528,93 +2077,46 @@ function flow(ab, bc, cd, de, ef, fg, gh, hi, ij) {
|
|
|
2528
2077
|
};
|
|
2529
2078
|
}
|
|
2530
2079
|
}
|
|
2531
|
-
/**
|
|
2532
|
-
* Creates a compile-time placeholder for a value of any type.
|
|
2533
|
-
*
|
|
2534
|
-
* **When to use**
|
|
2535
|
-
*
|
|
2536
|
-
* Use as a temporary typed placeholder while developing incomplete code.
|
|
2537
|
-
*
|
|
2538
|
-
* **Gotchas**
|
|
2539
|
-
*
|
|
2540
|
-
* `hole` is intended for temporary development use. If the placeholder is
|
|
2541
|
-
* evaluated at runtime, it throws.
|
|
2542
|
-
*
|
|
2543
|
-
* **Example** (Creating a development placeholder)
|
|
2544
|
-
*
|
|
2545
|
-
* ```ts
|
|
2546
|
-
* import { hole } from "effect"
|
|
2547
|
-
*
|
|
2548
|
-
* // Intentionally not called: `hole` throws if the placeholder is evaluated.
|
|
2549
|
-
* const buildUser = (id: number): { readonly id: number; readonly name: string } => ({
|
|
2550
|
-
* id,
|
|
2551
|
-
* name: hole<string>()
|
|
2552
|
-
* })
|
|
2553
|
-
*
|
|
2554
|
-
* console.log(typeof buildUser) // "function"
|
|
2555
|
-
* ```
|
|
2556
|
-
*
|
|
2557
|
-
* @category utility types
|
|
2558
|
-
* @since 2.0.0
|
|
2559
|
-
*/
|
|
2560
2080
|
const hole = cast(absurd);
|
|
2081
|
+
function not(predicate) {
|
|
2082
|
+
return (data) => !predicate(data);
|
|
2083
|
+
}
|
|
2084
|
+
const and = dual(2, (a, b) => (data) => a(data) && b(data));
|
|
2085
|
+
const or = dual(2, (a, b) => (data) => a(data) || b(data));
|
|
2086
|
+
const xor = dual(2, (a, b) => (data) => a(data) !== b(data));
|
|
2087
|
+
const eqv = dual(2, (a, b) => (data) => a(data) === b(data));
|
|
2088
|
+
const implies = dual(2, (antecedent, consequent) => (data) => !antecedent(data) || consequent(data));
|
|
2089
|
+
const nor = dual(2, (a, b) => (data) => !a(data) && !b(data));
|
|
2090
|
+
const nand = dual(2, (a, b) => (data) => !a(data) || !b(data));
|
|
2091
|
+
function isString(input) {
|
|
2092
|
+
return typeof input === "string";
|
|
2093
|
+
}
|
|
2094
|
+
function isFunction(input) {
|
|
2095
|
+
return typeof input === "function";
|
|
2096
|
+
}
|
|
2097
|
+
function isObjectKeyword(input) {
|
|
2098
|
+
return typeof input === "object" && input !== null || isFunction(input);
|
|
2099
|
+
}
|
|
2100
|
+
const hasProperty = dual(2, (data, property) => isObjectKeyword(data) && property in data);
|
|
2561
2101
|
function getOrInsertComputed(map, key, callback) {
|
|
2562
2102
|
if (map.has(key)) return map.get(key);
|
|
2563
2103
|
const value = callback(key);
|
|
2564
2104
|
map.set(key, value);
|
|
2565
2105
|
return value;
|
|
2566
2106
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
* Supports both data-first and data-last (`pipe`-friendly) call styles.
|
|
2571
|
-
*
|
|
2572
|
-
* @param pred - The predicate to test each element with.
|
|
2573
|
-
* @returns A new array without the matching prefix.
|
|
2574
|
-
* @example
|
|
2575
|
-
* ```ts
|
|
2576
|
-
* import * as assert from "node:assert"
|
|
2577
|
-
* import { dropWhile, pipe } from "@local/eff"
|
|
2578
|
-
*
|
|
2579
|
-
* // data-first
|
|
2580
|
-
* assert.deepStrictEqual(dropWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [3, 2, 1])
|
|
2581
|
-
*
|
|
2582
|
-
* // data-last
|
|
2583
|
-
* assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], dropWhile((n: number) => n < 3)), [3, 2, 1])
|
|
2584
|
-
* ```
|
|
2585
|
-
* @category array
|
|
2586
|
-
*/
|
|
2587
|
-
const dropWhile = dual(2, (xs, pred) => {
|
|
2588
|
-
const len = xs.length;
|
|
2107
|
+
const dropWhile = dual(2, (self, predicate) => {
|
|
2108
|
+
const input = Array.isArray(self) ? self : Array.from(self);
|
|
2109
|
+
const len = input.length;
|
|
2589
2110
|
let idx = 0;
|
|
2590
|
-
while (idx < len &&
|
|
2591
|
-
return
|
|
2111
|
+
while (idx < len && predicate(input[idx], idx)) idx++;
|
|
2112
|
+
return input.slice(idx);
|
|
2592
2113
|
});
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
* Supports both data-first and data-last (`pipe`-friendly) call styles.
|
|
2597
|
-
*
|
|
2598
|
-
* @param pred - The predicate to test each element with.
|
|
2599
|
-
* @returns A new array containing only the matching prefix.
|
|
2600
|
-
* @example
|
|
2601
|
-
* ```ts
|
|
2602
|
-
* import * as assert from "node:assert"
|
|
2603
|
-
* import { pipe, takeWhile } from "@local/eff"
|
|
2604
|
-
*
|
|
2605
|
-
* // data-first
|
|
2606
|
-
* assert.deepStrictEqual(takeWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [1, 2])
|
|
2607
|
-
*
|
|
2608
|
-
* // data-last
|
|
2609
|
-
* assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], takeWhile((n: number) => n < 3)), [1, 2])
|
|
2610
|
-
* ```
|
|
2611
|
-
* @category array
|
|
2612
|
-
*/
|
|
2613
|
-
const takeWhile = dual(2, (xs, pred) => {
|
|
2614
|
-
const len = xs.length;
|
|
2114
|
+
const takeWhile = dual(2, (self, predicate) => {
|
|
2115
|
+
const input = Array.isArray(self) ? self : Array.from(self);
|
|
2116
|
+
const len = input.length;
|
|
2615
2117
|
let idx = 0;
|
|
2616
|
-
while (idx < len &&
|
|
2617
|
-
return
|
|
2118
|
+
while (idx < len && predicate(input[idx], idx)) idx++;
|
|
2119
|
+
return input.slice(0, idx);
|
|
2618
2120
|
});
|
|
2619
2121
|
|
|
2620
2122
|
//#endregion
|
|
@@ -2694,7 +2196,7 @@ function create$38(context) {
|
|
|
2694
2196
|
const { body } = component.body;
|
|
2695
2197
|
for (const member of body) if (core.isComponentWillMount(member)) context.report({
|
|
2696
2198
|
fix(fixer) {
|
|
2697
|
-
if (!("key"
|
|
2199
|
+
if (!hasProperty(member, "key")) return null;
|
|
2698
2200
|
return fixer.replaceText(member.key, "UNSAFE_componentWillMount");
|
|
2699
2201
|
},
|
|
2700
2202
|
messageId: "default",
|
|
@@ -2727,7 +2229,7 @@ function create$37(context) {
|
|
|
2727
2229
|
const { body } = component.body;
|
|
2728
2230
|
for (const member of body) if (core.isComponentWillReceiveProps(member)) context.report({
|
|
2729
2231
|
fix(fixer) {
|
|
2730
|
-
if (!("key"
|
|
2232
|
+
if (!hasProperty(member, "key")) return null;
|
|
2731
2233
|
return fixer.replaceText(member.key, "UNSAFE_componentWillReceiveProps");
|
|
2732
2234
|
},
|
|
2733
2235
|
messageId: "default",
|
|
@@ -2760,7 +2262,7 @@ function create$36(context) {
|
|
|
2760
2262
|
const { body } = component.body;
|
|
2761
2263
|
for (const member of body) if (core.isComponentWillUpdate(member)) context.report({
|
|
2762
2264
|
fix(fixer) {
|
|
2763
|
-
if (!("key"
|
|
2265
|
+
if (!hasProperty(member, "key")) return null;
|
|
2764
2266
|
return fixer.replaceText(member.key, "UNSAFE_componentWillUpdate");
|
|
2765
2267
|
},
|
|
2766
2268
|
messageId: "default",
|
|
@@ -3005,14 +2507,6 @@ function create$31(context) {
|
|
|
3005
2507
|
});
|
|
3006
2508
|
} };
|
|
3007
2509
|
}
|
|
3008
|
-
/**
|
|
3009
|
-
* Determine whether the given CallExpression can be safely auto-fixed by replacing
|
|
3010
|
-
* the usage of `forwardRef` with passing `ref` as a prop
|
|
3011
|
-
*
|
|
3012
|
-
* @param context The rule context object
|
|
3013
|
-
* @param node The CallExpression node to check
|
|
3014
|
-
* @returns True if the call can be auto-fixed, false otherwise
|
|
3015
|
-
*/
|
|
3016
2510
|
function couldFix(context, node) {
|
|
3017
2511
|
const { importSource } = getSettingsFromContext(context);
|
|
3018
2512
|
const initialScope = context.sourceCode.getScope(node);
|
|
@@ -3023,12 +2517,6 @@ function couldFix(context, node) {
|
|
|
3023
2517
|
default: return false;
|
|
3024
2518
|
}
|
|
3025
2519
|
}
|
|
3026
|
-
/**
|
|
3027
|
-
* Generates the fix for the `forwardRef` call
|
|
3028
|
-
* @param context The rule context
|
|
3029
|
-
* @param node The `forwardRef` call expression
|
|
3030
|
-
* @returns A fixer function that applies the changes
|
|
3031
|
-
*/
|
|
3032
2520
|
function buildFix(context, node) {
|
|
3033
2521
|
return (fixer) => {
|
|
3034
2522
|
const [componentNode] = node.arguments;
|
|
@@ -3040,14 +2528,6 @@ function buildFix(context, node) {
|
|
|
3040
2528
|
];
|
|
3041
2529
|
};
|
|
3042
2530
|
}
|
|
3043
|
-
/**
|
|
3044
|
-
* Generates fixes for the component's props and ref arguments
|
|
3045
|
-
* @param context The rule context
|
|
3046
|
-
* @param fixer The rule fixer
|
|
3047
|
-
* @param node The function component node
|
|
3048
|
-
* @param typeArguments The type arguments from the `forwardRef` call
|
|
3049
|
-
* @returns An array of fixes for the component's signature
|
|
3050
|
-
*/
|
|
3051
2531
|
function buildFixForComponentProps(context, fixer, node, typeArguments) {
|
|
3052
2532
|
const getText = (node) => context.sourceCode.getText(node);
|
|
3053
2533
|
const [arg0, arg1] = node.params;
|
|
@@ -3093,22 +2573,6 @@ function buildFixForComponentProps(context, fixer, node, typeArguments) {
|
|
|
3093
2573
|
|
|
3094
2574
|
//#endregion
|
|
3095
2575
|
//#region src/utils/create-implicit-prop-listener.ts
|
|
3096
|
-
/**
|
|
3097
|
-
* Creates a listener that detects JSX spread attributes implicitly passing a given prop
|
|
3098
|
-
* (ex: 'children', 'key', 'ref') to a component.
|
|
3099
|
-
*
|
|
3100
|
-
* For each union constituent of the spread argument's type, the prop is reported unless:
|
|
3101
|
-
* 1. The prop symbol's fully qualified name is allowed (pass-through of React internally defined props)
|
|
3102
|
-
* 2. The fully qualified name of the prop's type symbol (or its alias) is allowed (React type aliases)
|
|
3103
|
-
*
|
|
3104
|
-
* @param context The ESLint rule context
|
|
3105
|
-
* @param options The prop name, the allow-list predicates (receive lowercased fully qualified names), and the report callback
|
|
3106
|
-
* @param options.name The name of the prop to detect
|
|
3107
|
-
* @param options.isAllowedProp A predicate that receives the prop symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
3108
|
-
* @param options.isAllowedType A predicate that receives the prop's type symbol's lowercased fully qualified name and returns `true` if it is allowed
|
|
3109
|
-
* @param options.onImplicitProp A callback invoked with the spread attribute node when an implicit prop is detected
|
|
3110
|
-
* @returns A rule listener reporting on JSX spread attributes
|
|
3111
|
-
*/
|
|
3112
2576
|
function createImplicitPropListener(context, options) {
|
|
3113
2577
|
const { name, isAllowedProp, isAllowedType, onImplicitProp } = options;
|
|
3114
2578
|
const services = ESLintUtils.getParserServices(context, false);
|
|
@@ -3244,12 +2708,6 @@ function create$27(context) {
|
|
|
3244
2708
|
...compare(version, "18.0.0", "<") ? [] : ["string", "falsy string"]
|
|
3245
2709
|
];
|
|
3246
2710
|
const services = ESLintUtils.getParserServices(context, false);
|
|
3247
|
-
/**
|
|
3248
|
-
* Recursively inspects a node to find potential leaked conditional rendering
|
|
3249
|
-
* @param node The AST node to inspect
|
|
3250
|
-
* @param seen A set of visited identifier names to prevent infinite recursion.
|
|
3251
|
-
* @returns A report descriptor if a problem is found, otherwise `null`
|
|
3252
|
-
*/
|
|
3253
2711
|
function visit(node, seen = /* @__PURE__ */ new Set()) {
|
|
3254
2712
|
if (node == null) return null;
|
|
3255
2713
|
if (Check.isJSXExpressionContainer(node)) return visit(node.expression, seen);
|
|
@@ -3387,21 +2845,11 @@ function create$25(context) {
|
|
|
3387
2845
|
|
|
3388
2846
|
//#endregion
|
|
3389
2847
|
//#region src/rules/no-missing-key/lib.ts
|
|
3390
|
-
/**
|
|
3391
|
-
* Iterator-like methods whose callback's return value becomes an item in a rendered list,
|
|
3392
|
-
* mapped to the position of the callback in the call's argument list.
|
|
3393
|
-
* `from` covers `Array.from(iterable, mapFn)`.
|
|
3394
|
-
*/
|
|
3395
2848
|
const INDEX_PARAM_POSITIONS = /* @__PURE__ */ new Map([
|
|
3396
2849
|
["flatMap", 0],
|
|
3397
2850
|
["from", 1],
|
|
3398
2851
|
["map", 0]
|
|
3399
2852
|
]);
|
|
3400
|
-
/**
|
|
3401
|
-
* Gets the nested return statements in the node that are within the same function
|
|
3402
|
-
* @param node The AST node
|
|
3403
|
-
* @returns The nested return statements in the node
|
|
3404
|
-
*/
|
|
3405
2853
|
function getNestedReturnStatements$1(node) {
|
|
3406
2854
|
const statements = [];
|
|
3407
2855
|
const boundary = Check.isFunction(node) ? node : Traverse.findParent(node, Check.isFunction);
|
|
@@ -3430,11 +2878,6 @@ var no_missing_key_default = createRule({
|
|
|
3430
2878
|
create: create$24,
|
|
3431
2879
|
defaultOptions: []
|
|
3432
2880
|
});
|
|
3433
|
-
/**
|
|
3434
|
-
* Gets the mapping callback of an iterator-like call expression (e.g. `.map(cb)`, `.flatMap(cb)`, `Array.from(it, cb)`).
|
|
3435
|
-
* @param node The call expression to inspect.
|
|
3436
|
-
* @returns The callback function node, or `null` if the call is not an iterator-like call with a function callback.
|
|
3437
|
-
*/
|
|
3438
2881
|
function getIteratorCallback(node) {
|
|
3439
2882
|
const callee = Extract.unwrap(node.callee);
|
|
3440
2883
|
if (callee.type !== AST_NODE_TYPES.MemberExpression) return null;
|
|
@@ -3447,12 +2890,6 @@ function getIteratorCallback(node) {
|
|
|
3447
2890
|
}
|
|
3448
2891
|
function create$24(context) {
|
|
3449
2892
|
let childrenToArrayDepth = 0;
|
|
3450
|
-
/**
|
|
3451
|
-
* Checks an expression that is rendered as an item of a list,
|
|
3452
|
-
* recursing into the branches it may evaluate to.
|
|
3453
|
-
* @param node The expression to check.
|
|
3454
|
-
* @returns The report descriptors for the violations found.
|
|
3455
|
-
*/
|
|
3456
2893
|
function visitItemExpression(node) {
|
|
3457
2894
|
switch (node.type) {
|
|
3458
2895
|
case AST_NODE_TYPES.ChainExpression:
|
|
@@ -3474,12 +2911,6 @@ function create$24(context) {
|
|
|
3474
2911
|
default: return [];
|
|
3475
2912
|
}
|
|
3476
2913
|
}
|
|
3477
|
-
/**
|
|
3478
|
-
* Checks every value an iterator callback may produce as a list item,
|
|
3479
|
-
* whether from an expression body or from return statements in a block body.
|
|
3480
|
-
* @param node The callback function to check.
|
|
3481
|
-
* @returns The report descriptors for the violations found.
|
|
3482
|
-
*/
|
|
3483
2914
|
function visitIteratorCallback(node) {
|
|
3484
2915
|
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return visitItemExpression(node.body);
|
|
3485
2916
|
return getNestedReturnStatements$1(node.body).flatMap((stmt) => stmt.argument == null ? [] : visitItemExpression(stmt.argument));
|
|
@@ -3510,23 +2941,9 @@ function create$24(context) {
|
|
|
3510
2941
|
|
|
3511
2942
|
//#endregion
|
|
3512
2943
|
//#region src/rules/no-misused-capture-owner-stack/lib.ts
|
|
3513
|
-
/**
|
|
3514
|
-
* Check if the given node is a member expression that accesses `process.env.NODE_ENV`
|
|
3515
|
-
* @param context The rule context
|
|
3516
|
-
* @param node The AST node
|
|
3517
|
-
* @returns True if the node is a member expression that accesses `process.env.NODE_ENV`, false otherwise
|
|
3518
|
-
*/
|
|
3519
2944
|
function isProcessEnvNodeEnv(context, node) {
|
|
3520
2945
|
return node != null && core.isAPI("process.env.NODE_ENV")(context, node);
|
|
3521
2946
|
}
|
|
3522
|
-
/**
|
|
3523
|
-
* Check if the given node is a binary expression that compares `process.env.NODE_ENV` with a string literal.
|
|
3524
|
-
* @param context The rule context
|
|
3525
|
-
* @param node The AST node
|
|
3526
|
-
* @param operator The operator used in the comparison
|
|
3527
|
-
* @param value The string literal value to compare against
|
|
3528
|
-
* @returns True if the node is a binary expression that compares `process.env.NODE_ENV` with the specified value, false otherwise
|
|
3529
|
-
*/
|
|
3530
2947
|
function isProcessEnvNodeEnvCompare(context, node, operator, value) {
|
|
3531
2948
|
if (node == null) return false;
|
|
3532
2949
|
if (node.type !== AST_NODE_TYPES.BinaryExpression) return false;
|
|
@@ -3588,44 +3005,21 @@ function create$23(context) {
|
|
|
3588
3005
|
|
|
3589
3006
|
//#endregion
|
|
3590
3007
|
//#region src/rules/no-nested-component-definitions/lib.ts
|
|
3591
|
-
/** Check if the node is a call expression to react-redux's `connect`. */
|
|
3592
3008
|
const isConnectCall = core.isAPICall("connect");
|
|
3593
|
-
/** Check if the node is a call expression to Relay's `createFragmentContainer`. */
|
|
3594
3009
|
const isCreateFragmentContainerCall = core.isAPICall("createFragmentContainer");
|
|
3595
|
-
/** Check if the node is a call expression to Relay's `createPaginationContainer`. */
|
|
3596
3010
|
const isCreatePaginationContainerCall = core.isAPICall("createPaginationContainer");
|
|
3597
|
-
/** Check if the node is a call expression to Relay's `createRefetchContainer`. */
|
|
3598
3011
|
const isCreateRefetchContainerCall = core.isAPICall("createRefetchContainer");
|
|
3599
|
-
/** Check if the node is a call expression to React's `forwardRef`. */
|
|
3600
3012
|
const isForwardRefCall = core.isAPICall("forwardRef");
|
|
3601
|
-
/** Check if the node is a call expression to a `graphql` tag function (e.g. from Relay or Apollo). */
|
|
3602
3013
|
const isGraphqlCall = core.isAPICall("graphql");
|
|
3603
|
-
/** Check if the node is a call expression to React's `memo`. */
|
|
3604
3014
|
const isMemoCall = core.isAPICall("memo");
|
|
3605
|
-
/** Check if the node is a call expression to MobX's `observer`. */
|
|
3606
3015
|
const isObserverCall = core.isAPICall("observer");
|
|
3607
|
-
/** Check if the node is a call expression to React's `useCallback`. */
|
|
3608
3016
|
const isUseCallbackCall = core.isAPICall("useCallback");
|
|
3609
|
-
/** Check if the node is a call expression to Formik's `withFormik`. */
|
|
3610
3017
|
const isWithFormikCall = core.isAPICall("withFormik");
|
|
3611
|
-
/** Check if the node is a call expression to recompose's `withHandlers`. */
|
|
3612
3018
|
const isWithHandlersCall = core.isAPICall("withHandlers");
|
|
3613
|
-
/** Check if the node is a call expression to recompose's `withLifecycle`. */
|
|
3614
3019
|
const isWithLifecycleCall = core.isAPICall("withLifecycle");
|
|
3615
|
-
/** Check if the node is a call expression to recompose's `withProps`. */
|
|
3616
3020
|
const isWithPropsCall = core.isAPICall("withProps");
|
|
3617
|
-
/** Check if the node is a call expression to react-router v5's `withRouter`. */
|
|
3618
3021
|
const isWithRouterCall = core.isAPICall("withRouter");
|
|
3619
|
-
/** Check if the node is a call expression to recompose's `withState`. */
|
|
3620
3022
|
const isWithStateCall = core.isAPICall("withState");
|
|
3621
|
-
/**
|
|
3622
|
-
* Check if a call is a well-known component wrapper call (e.g. `memo`, `connect`, `withFormik`).
|
|
3623
|
-
* Curried forms like `connect(...)(Component)` are matched via the innermost callee call.
|
|
3624
|
-
* @param context The rule context
|
|
3625
|
-
* @param call The call expression to check
|
|
3626
|
-
* @param arg The function node passed to the call
|
|
3627
|
-
* @returns `true` if the call is a component wrapper call
|
|
3628
|
-
*/
|
|
3629
3023
|
function isComponentWrapperCall(context, call, arg) {
|
|
3630
3024
|
if (Extract.unwrap(call.callee) === arg) return false;
|
|
3631
3025
|
call = getInnermostCall(call);
|
|
@@ -3646,12 +3040,6 @@ function isComponentWrapperCall(context, call, arg) {
|
|
|
3646
3040
|
if (isWithStateCall(context, call)) return true;
|
|
3647
3041
|
return false;
|
|
3648
3042
|
}
|
|
3649
|
-
/**
|
|
3650
|
-
* Unwrap curried call wrappers like `connect(...)(Component)` to get the innermost call expression.
|
|
3651
|
-
* Type expressions and chain expressions around each callee are unwrapped along the way.
|
|
3652
|
-
* @param node The outermost call expression to inspect.
|
|
3653
|
-
* @returns The innermost call expression, whose callee is not itself a call expression.
|
|
3654
|
-
*/
|
|
3655
3043
|
function getInnermostCall(node) {
|
|
3656
3044
|
let call = node;
|
|
3657
3045
|
let callee = Extract.unwrap(call.callee);
|
|
@@ -3661,12 +3049,6 @@ function getInnermostCall(node) {
|
|
|
3661
3049
|
}
|
|
3662
3050
|
return call;
|
|
3663
3051
|
}
|
|
3664
|
-
/**
|
|
3665
|
-
* Resolve the name a function is bound to through wrapping calls (e.g. `const C = useCallback(...)` → `C`).
|
|
3666
|
-
* @param context The rule context
|
|
3667
|
-
* @param node The function node to resolve the bound name for
|
|
3668
|
-
* @returns The bound name, or `null` if the call chain does not end at an identifier declarator
|
|
3669
|
-
*/
|
|
3670
3052
|
function getWrapperCallBoundName(context, node) {
|
|
3671
3053
|
let current = node;
|
|
3672
3054
|
let parent = current.parent;
|
|
@@ -3685,20 +3067,9 @@ function getWrapperCallBoundName(context, node) {
|
|
|
3685
3067
|
if (parent.type !== AST_NODE_TYPES.VariableDeclarator || !Check.isIdentifier(parent.id)) return null;
|
|
3686
3068
|
return parent.id.name;
|
|
3687
3069
|
}
|
|
3688
|
-
/**
|
|
3689
|
-
* Check if the node is inside a JSX attribute value
|
|
3690
|
-
* @param node The AST node to check
|
|
3691
|
-
* @returns `true` if the node is inside a JSX attribute value
|
|
3692
|
-
*/
|
|
3693
3070
|
function isInsideJSXAttributeValue(node) {
|
|
3694
3071
|
return node.parent.type === AST_NODE_TYPES.JSXAttribute || findParentAttribute(node, (n) => n.value?.type === AST_NODE_TYPES.JSXExpressionContainer) != null;
|
|
3695
3072
|
}
|
|
3696
|
-
/**
|
|
3697
|
-
* Check if the node is declared inside a class component's render block
|
|
3698
|
-
* Ex: class C extends React.Component { render() { const Nested = () => <div />; } }
|
|
3699
|
-
* @param node The AST node to check
|
|
3700
|
-
* @returns `true` if the node is inside a class component's render block
|
|
3701
|
-
*/
|
|
3702
3073
|
function isInsideRenderMethod(node) {
|
|
3703
3074
|
return Traverse.findParent(node, (n) => core.isRenderMethodLike(n) && core.isClassComponent(n.parent.parent)) != null;
|
|
3704
3075
|
}
|
|
@@ -4066,7 +3437,7 @@ function isContextName(name, isReact18OrBelow) {
|
|
|
4066
3437
|
}
|
|
4067
3438
|
function getHumanReadableKind$1(node) {
|
|
4068
3439
|
if (node.type === AST_NODE_TYPES.Literal) {
|
|
4069
|
-
if ("regex"
|
|
3440
|
+
if (hasProperty(node, "regex")) return "regexp literal";
|
|
4070
3441
|
if (node.value === null) return "null literal";
|
|
4071
3442
|
return `${typeof node.value} literal`;
|
|
4072
3443
|
}
|
|
@@ -4103,7 +3474,7 @@ function create$13(context) {
|
|
|
4103
3474
|
if (enclosingFunction == null) return;
|
|
4104
3475
|
if (compilationMode === "annotation" && core.isFunctionHasDirective(enclosingFunction, "use memo")) return;
|
|
4105
3476
|
const attribute = node.attributes.find((attribute) => attribute.type === AST_NODE_TYPES.JSXAttribute && attribute.name.name === "value");
|
|
4106
|
-
if (attribute == null || !("value"
|
|
3477
|
+
if (attribute == null || !hasProperty(attribute, "value")) return;
|
|
4107
3478
|
const value = attribute.value;
|
|
4108
3479
|
if (value?.type !== AST_NODE_TYPES.JSXExpressionContainer) return;
|
|
4109
3480
|
const valueExpression = value.expression;
|
|
@@ -4141,7 +3512,7 @@ const SEL_OBJECT_DESTRUCTURING_VARIABLE_DECLARATOR = [
|
|
|
4141
3512
|
].join("");
|
|
4142
3513
|
function getHumanReadableKind(node) {
|
|
4143
3514
|
if (node.type === AST_NODE_TYPES.Literal) {
|
|
4144
|
-
if ("regex"
|
|
3515
|
+
if (hasProperty(node, "regex")) return "regexp literal";
|
|
4145
3516
|
if (node.value === null) return "null literal";
|
|
4146
3517
|
return `${typeof node.value} literal`;
|
|
4147
3518
|
}
|
|
@@ -4528,9 +3899,6 @@ function create$8(context) {
|
|
|
4528
3899
|
|
|
4529
3900
|
//#endregion
|
|
4530
3901
|
//#region src/rules/purity/lib.ts
|
|
4531
|
-
/**
|
|
4532
|
-
* Known impure functions
|
|
4533
|
-
*/
|
|
4534
3902
|
const IMPURE_FUNCS = /* @__PURE__ */ new Map([
|
|
4535
3903
|
["Atomics", /* @__PURE__ */ new Set([
|
|
4536
3904
|
"add",
|
|
@@ -4781,9 +4149,6 @@ const IMPURE_FUNCS = /* @__PURE__ */ new Map([
|
|
|
4781
4149
|
"structuredClone"
|
|
4782
4150
|
])]
|
|
4783
4151
|
]);
|
|
4784
|
-
/**
|
|
4785
|
-
* Known impure global constructors used with `new`
|
|
4786
|
-
*/
|
|
4787
4152
|
const IMPURE_CTORS = /* @__PURE__ */ new Set([
|
|
4788
4153
|
"Audio",
|
|
4789
4154
|
"AudioContext",
|
|
@@ -4809,9 +4174,6 @@ const IMPURE_CTORS = /* @__PURE__ */ new Set([
|
|
|
4809
4174
|
"Worker",
|
|
4810
4175
|
"XMLHttpRequest"
|
|
4811
4176
|
]);
|
|
4812
|
-
/**
|
|
4813
|
-
* Known pure functions
|
|
4814
|
-
*/
|
|
4815
4177
|
const PURE_FUNCS = /* @__PURE__ */ new Map([
|
|
4816
4178
|
["Array", /* @__PURE__ */ new Set([
|
|
4817
4179
|
"from",
|
|
@@ -4919,18 +4281,9 @@ const PURE_FUNCS = /* @__PURE__ */ new Map([
|
|
|
4919
4281
|
function isCatalogObject(name) {
|
|
4920
4282
|
return IMPURE_FUNCS.has(name) || PURE_FUNCS.has(name);
|
|
4921
4283
|
}
|
|
4922
|
-
/**
|
|
4923
|
-
* Check if the program has a top-level `use client` directive, marking the
|
|
4924
|
-
* module as a client module in React Server Components conventions.
|
|
4925
|
-
*/
|
|
4926
4284
|
function hasUseClientDirective(program) {
|
|
4927
4285
|
return program.body.some((stmt) => stmt.type === AST_NODE_TYPES.ExpressionStatement && stmt.directive === "use client");
|
|
4928
4286
|
}
|
|
4929
|
-
/**
|
|
4930
|
-
* Find the property key under which `name` is bound in an object destructuring
|
|
4931
|
-
* pattern, e.g. `"random"` for `random` in `const { random } = Math` or for
|
|
4932
|
-
* `r` in `const { random: r } = Math`.
|
|
4933
|
-
*/
|
|
4934
4287
|
function getDestructuredPropertyName(pattern, name) {
|
|
4935
4288
|
for (const property of pattern.properties) {
|
|
4936
4289
|
if (property.type !== AST_NODE_TYPES.Property) continue;
|
|
@@ -4939,13 +4292,6 @@ function getDestructuredPropertyName(pattern, name) {
|
|
|
4939
4292
|
}
|
|
4940
4293
|
return null;
|
|
4941
4294
|
}
|
|
4942
|
-
/**
|
|
4943
|
-
* Resolve a member expression to a builtin object name and property, e.g.
|
|
4944
|
-
* `window.Math.random` resolves to `{ object: "Math", property: "random" }`.
|
|
4945
|
-
* Intermediate segments are followed only while they name known catalog
|
|
4946
|
-
* objects (`window`, `Math`, ...), so unknown-global chains like
|
|
4947
|
-
* `foo.Math.random` do not resolve.
|
|
4948
|
-
*/
|
|
4949
4295
|
function resolveBuiltinMember(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
4950
4296
|
const chain = Extract.getMemberChain(node);
|
|
4951
4297
|
const rootId = chain.at(0);
|
|
@@ -4965,17 +4311,6 @@ function resolveBuiltinMember(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
|
4965
4311
|
property: names.at(-1) ?? null
|
|
4966
4312
|
};
|
|
4967
4313
|
}
|
|
4968
|
-
/**
|
|
4969
|
-
* Recursively resolve an identifier to a builtin global object name and
|
|
4970
|
-
* optional property. Follows simple assignment chains like `const M = Math`,
|
|
4971
|
-
* member-function aliases like `const random = Math.random`, and destructured
|
|
4972
|
-
* aliases like `const { random } = Math`.
|
|
4973
|
-
* Returns `null` if the identifier is locally defined (parameter, import, function declaration, etc.)
|
|
4974
|
-
* or resolves to a non-builtin source.
|
|
4975
|
-
* @param context - The rule context.
|
|
4976
|
-
* @param node - The identifier node to resolve.
|
|
4977
|
-
* @param seen - A set of already visited identifier names to prevent infinite loops.
|
|
4978
|
-
*/
|
|
4979
4314
|
function resolveBuiltinObjectName(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
4980
4315
|
if (seen.has(node.name)) return null;
|
|
4981
4316
|
seen.add(node.name);
|
|
@@ -5147,13 +4482,6 @@ function getRefAccess(node) {
|
|
|
5147
4482
|
node
|
|
5148
4483
|
};
|
|
5149
4484
|
}
|
|
5150
|
-
/**
|
|
5151
|
-
* Check whether `node` (a `ref.current` MemberExpression) is assigned through a
|
|
5152
|
-
* destructuring pattern or a for-in/of loop target, e.g.
|
|
5153
|
-
* `({ a: ref.current } = value)`, `[ref.current] = value`,
|
|
5154
|
-
* `({ a: ref.current.x } = value)`, or `for (ref.current of items)`. Defaults
|
|
5155
|
-
* and computed keys inside patterns are reads, not writes.
|
|
5156
|
-
*/
|
|
5157
4485
|
function isPatternWriteTarget(node) {
|
|
5158
4486
|
let child = node;
|
|
5159
4487
|
let current = node.parent;
|
|
@@ -5180,10 +4508,6 @@ function isPatternWriteTarget(node) {
|
|
|
5180
4508
|
current = current.parent;
|
|
5181
4509
|
}
|
|
5182
4510
|
}
|
|
5183
|
-
/**
|
|
5184
|
-
* Check whether `node` (a `ref.current` MemberExpression) is being written to indirectly through
|
|
5185
|
-
* a nested property write, e.g. `ref.current.inner = value` or `ref.current.inner++`.
|
|
5186
|
-
*/
|
|
5187
4511
|
function isNestedRefCurrentWrite(node) {
|
|
5188
4512
|
let outer = node;
|
|
5189
4513
|
let sawNesting = false;
|
|
@@ -5202,11 +4526,6 @@ function isNestedRefCurrentWrite(node) {
|
|
|
5202
4526
|
return false;
|
|
5203
4527
|
}
|
|
5204
4528
|
}
|
|
5205
|
-
/**
|
|
5206
|
-
* Parse an exact nullish check and return the branch where the checked value is null/undefined.
|
|
5207
|
-
* Truthiness checks such as `!ref.current` are deliberately excluded: falsy ref values are not
|
|
5208
|
-
* necessarily uninitialized.
|
|
5209
|
-
*/
|
|
5210
4529
|
function getNullCheckBranch(test, isCheckedValue, isNullishValue) {
|
|
5211
4530
|
const unwrapped = Extract.unwrap(test);
|
|
5212
4531
|
if (unwrapped.type === AST_NODE_TYPES.UnaryExpression && unwrapped.operator === "!") {
|
|
@@ -5245,7 +4564,6 @@ function getGuardDisposition(node, getNullBranch) {
|
|
|
5245
4564
|
}
|
|
5246
4565
|
return null;
|
|
5247
4566
|
}
|
|
5248
|
-
/** Return whether every path through a statement exits the current function. */
|
|
5249
4567
|
function isUnconditionallyTerminating(statement) {
|
|
5250
4568
|
switch (statement.type) {
|
|
5251
4569
|
case AST_NODE_TYPES.ReturnStatement:
|
|
@@ -5279,14 +4597,6 @@ function isAfterTerminatingNonNullGuard(node, getNullBranch) {
|
|
|
5279
4597
|
|
|
5280
4598
|
//#endregion
|
|
5281
4599
|
//#region src/rules/refs/collect.ts
|
|
5282
|
-
/**
|
|
5283
|
-
* Collect the raw facts the refs rule reasons about: identifier/member/function
|
|
5284
|
-
* bindings with their source positions, JSX `ref` attribute targets, call
|
|
5285
|
-
* expressions, ref pass sites (calls, constructor calls, tagged templates), and
|
|
5286
|
-
* every `.current` member access. Facts outside any function are never
|
|
5287
|
-
* reachable during render and are not collected. All inference is deferred to
|
|
5288
|
-
* `Program:exit`.
|
|
5289
|
-
*/
|
|
5290
4600
|
function createFactCollector$4() {
|
|
5291
4601
|
const facts = {
|
|
5292
4602
|
bindings: [],
|
|
@@ -5386,11 +4696,9 @@ function createFactCollector$4() {
|
|
|
5386
4696
|
|
|
5387
4697
|
//#endregion
|
|
5388
4698
|
//#region src/rules/refs/effects.ts
|
|
5389
|
-
/** The nearest strict ancestor of `node` that is a component or hook. */
|
|
5390
4699
|
function findBoundary(node, boundaries) {
|
|
5391
4700
|
return Traverse.findParent(node, (candidate) => Check.isFunction(candidate) && boundaries.has(candidate));
|
|
5392
4701
|
}
|
|
5393
|
-
/** The boundary containing `node`, where `node` is the nearest enclosing function of the fact. */
|
|
5394
4702
|
function getBoundaryOf(node, boundaries) {
|
|
5395
4703
|
return boundaries.has(node) ? node : findBoundary(node, boundaries);
|
|
5396
4704
|
}
|
|
@@ -5398,12 +4706,6 @@ function isReachedDuringRender(node, boundary, reachability) {
|
|
|
5398
4706
|
const reached = reachability.get(boundary);
|
|
5399
4707
|
return reached != null && isReachedThroughFunctions(node, boundary, reached);
|
|
5400
4708
|
}
|
|
5401
|
-
/**
|
|
5402
|
-
* Resolve the collected call edges into a per-boundary render call graph.
|
|
5403
|
-
* Direct calls, IIFEs, known synchronous callbacks, function declarations,
|
|
5404
|
-
* aliases, and simple object-property bindings all share the same
|
|
5405
|
-
* identity-based resolver. Each call target is resolved exactly once.
|
|
5406
|
-
*/
|
|
5407
4709
|
function inferCallGraph(boundaries, callEdges, resolveCallable) {
|
|
5408
4710
|
const callGraph = /* @__PURE__ */ new Map();
|
|
5409
4711
|
for (const edge of callEdges) {
|
|
@@ -5426,10 +4728,6 @@ function inferCallGraph(boundaries, callEdges, resolveCallable) {
|
|
|
5426
4728
|
}
|
|
5427
4729
|
return callGraph;
|
|
5428
4730
|
}
|
|
5429
|
-
/**
|
|
5430
|
-
* Walk the call graph from each render boundary and gather every function
|
|
5431
|
-
* reached during that boundary's render.
|
|
5432
|
-
*/
|
|
5433
4731
|
function collectReachableFunctions(boundaries, callGraph) {
|
|
5434
4732
|
const reachedByBoundary = /* @__PURE__ */ new Map();
|
|
5435
4733
|
for (const boundary of boundaries) {
|
|
@@ -5446,10 +4744,6 @@ function collectReachableFunctions(boundaries, callGraph) {
|
|
|
5446
4744
|
}
|
|
5447
4745
|
return reachedByBoundary;
|
|
5448
4746
|
}
|
|
5449
|
-
/**
|
|
5450
|
-
* Validate the reached `.current` accesses and track the one permitted lazy
|
|
5451
|
-
* initialization per ref.
|
|
5452
|
-
*/
|
|
5453
4747
|
function inferRefViolations(accesses, boundaries, reachability, resolver) {
|
|
5454
4748
|
const violations = [];
|
|
5455
4749
|
const initializedRefs = /* @__PURE__ */ new Map();
|
|
@@ -5486,31 +4780,18 @@ function inferRefViolations(accesses, boundaries, reachability, resolver) {
|
|
|
5486
4780
|
}
|
|
5487
4781
|
return violations;
|
|
5488
4782
|
}
|
|
5489
|
-
/**
|
|
5490
|
-
* Get the statically known name of a pass-site callee, whether it is a plain
|
|
5491
|
-
* call, a constructor call, or a tagged template.
|
|
5492
|
-
*/
|
|
5493
4783
|
function getPassSiteCalleeName(node) {
|
|
5494
4784
|
const callee = Extract.unwrap(node.type === AST_NODE_TYPES.TaggedTemplateExpression ? node.tag : node.callee);
|
|
5495
4785
|
if (Check.isIdentifier(callee)) return callee.name;
|
|
5496
4786
|
if (callee.type === AST_NODE_TYPES.MemberExpression && !callee.computed && Check.isIdentifier(callee.property)) return callee.property.name;
|
|
5497
4787
|
return null;
|
|
5498
4788
|
}
|
|
5499
|
-
/**
|
|
5500
|
-
* The `mergeRefs` exemption is name-based but alias-aware: a simple variable
|
|
5501
|
-
* alias of a `mergeRefs` binding keeps the exemption.
|
|
5502
|
-
*/
|
|
5503
4789
|
function isMergeRefsCallee(callee, calleeName, position, resolver) {
|
|
5504
4790
|
if (calleeName === "mergeRefs") return true;
|
|
5505
4791
|
if (callee.type !== AST_NODE_TYPES.Identifier) return false;
|
|
5506
4792
|
const variable = resolver.getVariable(callee);
|
|
5507
4793
|
return variable != null && resolver.resolveAliasTarget(variable, position).name === "mergeRefs";
|
|
5508
4794
|
}
|
|
5509
|
-
/**
|
|
5510
|
-
* Passing a ref to an unknown function can expose its value during render.
|
|
5511
|
-
* Hook callbacks, mergeRefs, and the existing render-prop compatibility case
|
|
5512
|
-
* remain exempt.
|
|
5513
|
-
*/
|
|
5514
4795
|
function inferRefPassViolations(passSites, boundaries, reachability, resolver) {
|
|
5515
4796
|
const violations = [];
|
|
5516
4797
|
for (const site of passSites) {
|
|
@@ -5540,11 +4821,6 @@ function inferRefPassViolations(passSites, boundaries, reachability, resolver) {
|
|
|
5540
4821
|
|
|
5541
4822
|
//#endregion
|
|
5542
4823
|
//#region src/rules/refs/origins.ts
|
|
5543
|
-
/**
|
|
5544
|
-
* Resolve which variables hold refs and which resolve to callable functions.
|
|
5545
|
-
* Maps are keyed by ESLint variables, not source names, so shadowed
|
|
5546
|
-
* identifiers and separate components cannot contaminate each other.
|
|
5547
|
-
*/
|
|
5548
4824
|
function createBindingResolver(context, facts) {
|
|
5549
4825
|
const { additionalRefHooks } = getSettingsFromContext(context);
|
|
5550
4826
|
const bindings = /* @__PURE__ */ new Map();
|
|
@@ -5637,11 +4913,6 @@ function createBindingResolver(context, facts) {
|
|
|
5637
4913
|
case "unknown": return null;
|
|
5638
4914
|
}
|
|
5639
4915
|
}
|
|
5640
|
-
/**
|
|
5641
|
-
* Follow simple variable-to-variable aliases to the terminal variable, e.g.
|
|
5642
|
-
* `const combine = mergeRefs` resolves to the `mergeRefs` variable itself.
|
|
5643
|
-
* Position-aware, so a reassigned alias resolves to its latest source.
|
|
5644
|
-
*/
|
|
5645
4916
|
function resolveAliasTarget(variable, position, seen = /* @__PURE__ */ new Set()) {
|
|
5646
4917
|
if (seen.has(variable)) return variable;
|
|
5647
4918
|
seen.add(variable);
|
|
@@ -5753,38 +5024,17 @@ function create$6(context) {
|
|
|
5753
5024
|
function assert(cond, message) {
|
|
5754
5025
|
if (!cond) throw new Error(message ?? "Assertion violated.");
|
|
5755
5026
|
}
|
|
5756
|
-
/**
|
|
5757
|
-
* Checks whether or not a given segment is reachable.
|
|
5758
|
-
*/
|
|
5759
5027
|
function isReachable$1(segment) {
|
|
5760
5028
|
return segment.reachable;
|
|
5761
5029
|
}
|
|
5762
|
-
/**
|
|
5763
|
-
* A code path segment.
|
|
5764
|
-
*/
|
|
5765
5030
|
var CodePathSegment = class CodePathSegment {
|
|
5766
|
-
/**
|
|
5767
|
-
* The identifier of this code path.
|
|
5768
|
-
* Rules use it to store additional information of each rule.
|
|
5769
|
-
*/
|
|
5770
5031
|
id;
|
|
5771
|
-
/** An array of the next reachable segments. */
|
|
5772
5032
|
nextSegments;
|
|
5773
|
-
/** An array of the previous reachable segments. */
|
|
5774
5033
|
prevSegments;
|
|
5775
|
-
/** An array of all next segments (including unreachable). */
|
|
5776
5034
|
allNextSegments;
|
|
5777
|
-
/** An array of all previous segments (including unreachable). */
|
|
5778
5035
|
allPrevSegments;
|
|
5779
|
-
/** Whether this segment is reachable. */
|
|
5780
5036
|
reachable;
|
|
5781
|
-
/** Internal bookkeeping data. */
|
|
5782
5037
|
internal;
|
|
5783
|
-
/**
|
|
5784
|
-
* @param id An identifier.
|
|
5785
|
-
* @param allPrevSegments An array of the previous segments (including unreachable).
|
|
5786
|
-
* @param reachable Whether this segment is reachable.
|
|
5787
|
-
*/
|
|
5788
5038
|
constructor(id, allPrevSegments, reachable) {
|
|
5789
5039
|
this.id = id;
|
|
5790
5040
|
this.nextSegments = [];
|
|
@@ -5797,44 +5047,23 @@ var CodePathSegment = class CodePathSegment {
|
|
|
5797
5047
|
loopedPrevSegments: []
|
|
5798
5048
|
};
|
|
5799
5049
|
}
|
|
5800
|
-
/**
|
|
5801
|
-
* Checks if a given previous segment is coming from the end of a loop.
|
|
5802
|
-
*/
|
|
5803
5050
|
isLoopedPrevSegment(segment) {
|
|
5804
5051
|
return this.internal.loopedPrevSegments.includes(segment);
|
|
5805
5052
|
}
|
|
5806
|
-
/**
|
|
5807
|
-
* Creates the root segment.
|
|
5808
|
-
*/
|
|
5809
5053
|
static newRoot(id) {
|
|
5810
5054
|
return new CodePathSegment(id, [], true);
|
|
5811
5055
|
}
|
|
5812
|
-
/**
|
|
5813
|
-
* Creates a segment that follows given segments.
|
|
5814
|
-
*/
|
|
5815
5056
|
static newNext(id, allPrevSegments) {
|
|
5816
5057
|
return new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), allPrevSegments.some(isReachable$1));
|
|
5817
5058
|
}
|
|
5818
|
-
/**
|
|
5819
|
-
* Creates an unreachable segment that follows given segments.
|
|
5820
|
-
*/
|
|
5821
5059
|
static newUnreachable(id, allPrevSegments) {
|
|
5822
5060
|
const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
|
|
5823
5061
|
CodePathSegment.markUsed(segment);
|
|
5824
5062
|
return segment;
|
|
5825
5063
|
}
|
|
5826
|
-
/**
|
|
5827
|
-
* Creates a segment that follows given segments.
|
|
5828
|
-
* This factory method does not connect with `allPrevSegments`.
|
|
5829
|
-
* But this inherits `reachable` flag.
|
|
5830
|
-
*/
|
|
5831
5064
|
static newDisconnected(id, allPrevSegments) {
|
|
5832
5065
|
return new CodePathSegment(id, [], allPrevSegments.some(isReachable$1));
|
|
5833
5066
|
}
|
|
5834
|
-
/**
|
|
5835
|
-
* Makes a given segment being used.
|
|
5836
|
-
* Also registers the segment into the previous segments as a next.
|
|
5837
|
-
*/
|
|
5838
5067
|
static markUsed(segment) {
|
|
5839
5068
|
if (segment.internal.used) return;
|
|
5840
5069
|
segment.internal.used = true;
|
|
@@ -5846,15 +5075,9 @@ var CodePathSegment = class CodePathSegment {
|
|
|
5846
5075
|
}
|
|
5847
5076
|
else for (i = 0; i < segment.allPrevSegments.length; ++i) segment.allPrevSegments[i].allNextSegments.push(segment);
|
|
5848
5077
|
}
|
|
5849
|
-
/**
|
|
5850
|
-
* Marks a previous segment as looped.
|
|
5851
|
-
*/
|
|
5852
5078
|
static markPrevSegmentAsLooped(segment, prevSegment) {
|
|
5853
5079
|
segment.internal.loopedPrevSegments.push(prevSegment);
|
|
5854
5080
|
}
|
|
5855
|
-
/**
|
|
5856
|
-
* Replaces unused segments with the previous segments of each unused segment.
|
|
5857
|
-
*/
|
|
5858
5081
|
static flattenUnusedSegments(segments) {
|
|
5859
5082
|
const done = Object.create(null);
|
|
5860
5083
|
const retv = [];
|
|
@@ -5876,19 +5099,9 @@ var CodePathSegment = class CodePathSegment {
|
|
|
5876
5099
|
return retv;
|
|
5877
5100
|
}
|
|
5878
5101
|
};
|
|
5879
|
-
/**
|
|
5880
|
-
* Gets whether or not a given segment is reachable.
|
|
5881
|
-
*/
|
|
5882
5102
|
function isReachable(segment) {
|
|
5883
5103
|
return segment.reachable;
|
|
5884
5104
|
}
|
|
5885
|
-
/**
|
|
5886
|
-
* Creates new segments from the specific range of `context.segmentsList`.
|
|
5887
|
-
*
|
|
5888
|
-
* When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
|
|
5889
|
-
* `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
|
|
5890
|
-
* This `h` is from `b`, `d`, and `f`.
|
|
5891
|
-
*/
|
|
5892
5105
|
function makeSegments(context, begin, end, create) {
|
|
5893
5106
|
const list = context.segmentsList;
|
|
5894
5107
|
const normalizedBegin = begin >= 0 ? begin : list.length + begin;
|
|
@@ -5901,12 +5114,6 @@ function makeSegments(context, begin, end, create) {
|
|
|
5901
5114
|
}
|
|
5902
5115
|
return segments;
|
|
5903
5116
|
}
|
|
5904
|
-
/**
|
|
5905
|
-
* `segments` becomes doubly in a `finally` block. Then if a code path exits by a
|
|
5906
|
-
* control statement (such as `break`, `continue`) from the `finally` block, the
|
|
5907
|
-
* destination's segments may be half of the source segments. In that case, this
|
|
5908
|
-
* merges segments.
|
|
5909
|
-
*/
|
|
5910
5117
|
function mergeExtraSegments(context, segments) {
|
|
5911
5118
|
let currentSegments = segments;
|
|
5912
5119
|
while (currentSegments.length > context.count) {
|
|
@@ -5916,9 +5123,6 @@ function mergeExtraSegments(context, segments) {
|
|
|
5916
5123
|
}
|
|
5917
5124
|
return currentSegments;
|
|
5918
5125
|
}
|
|
5919
|
-
/**
|
|
5920
|
-
* A class to manage forking.
|
|
5921
|
-
*/
|
|
5922
5126
|
var ForkContext = class ForkContext {
|
|
5923
5127
|
idGenerator;
|
|
5924
5128
|
upper;
|
|
@@ -5930,91 +5134,51 @@ var ForkContext = class ForkContext {
|
|
|
5930
5134
|
this.count = count;
|
|
5931
5135
|
this.segmentsList = [];
|
|
5932
5136
|
}
|
|
5933
|
-
/** The head segments. */
|
|
5934
5137
|
get head() {
|
|
5935
5138
|
const list = this.segmentsList;
|
|
5936
5139
|
return list.length === 0 ? [] : list[list.length - 1];
|
|
5937
5140
|
}
|
|
5938
|
-
/** Whether the segments list is empty. */
|
|
5939
5141
|
get empty() {
|
|
5940
5142
|
return this.segmentsList.length === 0;
|
|
5941
5143
|
}
|
|
5942
|
-
/** Whether any head segment is reachable. */
|
|
5943
5144
|
get reachable() {
|
|
5944
5145
|
const segments = this.head;
|
|
5945
5146
|
return segments.length > 0 && segments.some(isReachable);
|
|
5946
5147
|
}
|
|
5947
|
-
/**
|
|
5948
|
-
* Creates new segments from this context.
|
|
5949
|
-
*/
|
|
5950
5148
|
makeNext(begin, end) {
|
|
5951
5149
|
return makeSegments(this, begin, end, CodePathSegment.newNext);
|
|
5952
5150
|
}
|
|
5953
|
-
/**
|
|
5954
|
-
* Creates new unreachable segments from this context.
|
|
5955
|
-
*/
|
|
5956
5151
|
makeUnreachable(begin, end) {
|
|
5957
5152
|
return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
|
|
5958
5153
|
}
|
|
5959
|
-
/**
|
|
5960
|
-
* Creates new disconnected segments from this context.
|
|
5961
|
-
* The new segments don't have connections for previous segments,
|
|
5962
|
-
* but inherit the reachable flag.
|
|
5963
|
-
*/
|
|
5964
5154
|
makeDisconnected(begin, end) {
|
|
5965
5155
|
return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
|
|
5966
5156
|
}
|
|
5967
|
-
/**
|
|
5968
|
-
* Adds segments into this context. The added segments become the head.
|
|
5969
|
-
*/
|
|
5970
5157
|
add(segments) {
|
|
5971
5158
|
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
|
|
5972
5159
|
this.segmentsList.push(mergeExtraSegments(this, segments));
|
|
5973
5160
|
}
|
|
5974
|
-
/**
|
|
5975
|
-
* Replaces the head segments with given segments.
|
|
5976
|
-
* The current head segments are removed.
|
|
5977
|
-
*/
|
|
5978
5161
|
replaceHead(segments) {
|
|
5979
5162
|
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
|
|
5980
5163
|
this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
|
|
5981
5164
|
}
|
|
5982
|
-
/**
|
|
5983
|
-
* Adds all segments of a given fork context into this context.
|
|
5984
|
-
*/
|
|
5985
5165
|
addAll(context) {
|
|
5986
5166
|
assert(context.count === this.count);
|
|
5987
5167
|
const source = context.segmentsList;
|
|
5988
5168
|
for (let i = 0; i < source.length; ++i) this.segmentsList.push(source[i]);
|
|
5989
5169
|
}
|
|
5990
|
-
/**
|
|
5991
|
-
* Clears all segments in this context.
|
|
5992
|
-
*/
|
|
5993
5170
|
clear() {
|
|
5994
5171
|
this.segmentsList = [];
|
|
5995
5172
|
}
|
|
5996
|
-
/**
|
|
5997
|
-
* Creates the root fork context.
|
|
5998
|
-
*/
|
|
5999
5173
|
static newRoot(idGenerator) {
|
|
6000
5174
|
const context = new ForkContext(idGenerator, null, 1);
|
|
6001
5175
|
context.add([CodePathSegment.newRoot(idGenerator.next())]);
|
|
6002
5176
|
return context;
|
|
6003
5177
|
}
|
|
6004
|
-
/**
|
|
6005
|
-
* Creates an empty fork context preceded by a given context.
|
|
6006
|
-
*/
|
|
6007
5178
|
static newEmpty(parentContext, forkLeavingPath) {
|
|
6008
5179
|
return new ForkContext(parentContext.idGenerator, parentContext, (forkLeavingPath ? 2 : 1) * parentContext.count);
|
|
6009
5180
|
}
|
|
6010
5181
|
};
|
|
6011
|
-
/**
|
|
6012
|
-
* Adds given segments into the `dest` array.
|
|
6013
|
-
* If the `others` array does not include the given segments, adds to the `all`
|
|
6014
|
-
* array as well.
|
|
6015
|
-
*
|
|
6016
|
-
* This adds only reachable and used segments.
|
|
6017
|
-
*/
|
|
6018
5182
|
function addToReturnedOrThrown(dest, others, all, segments) {
|
|
6019
5183
|
for (let i = 0; i < segments.length; ++i) {
|
|
6020
5184
|
const segment = segments[i];
|
|
@@ -6022,9 +5186,6 @@ function addToReturnedOrThrown(dest, others, all, segments) {
|
|
|
6022
5186
|
if (!others.includes(segment)) all.push(segment);
|
|
6023
5187
|
}
|
|
6024
5188
|
}
|
|
6025
|
-
/**
|
|
6026
|
-
* Gets a loop-context for a `continue` statement.
|
|
6027
|
-
*/
|
|
6028
5189
|
function getContinueContext(state, label) {
|
|
6029
5190
|
if (!label) return state.loopContext;
|
|
6030
5191
|
let context = state.loopContext;
|
|
@@ -6035,9 +5196,6 @@ function getContinueContext(state, label) {
|
|
|
6035
5196
|
/* c8 ignore next */
|
|
6036
5197
|
return null;
|
|
6037
5198
|
}
|
|
6038
|
-
/**
|
|
6039
|
-
* Gets a context for a `break` statement.
|
|
6040
|
-
*/
|
|
6041
5199
|
function getBreakContext(state, label) {
|
|
6042
5200
|
let context = state.breakContext;
|
|
6043
5201
|
while (context) {
|
|
@@ -6047,9 +5205,6 @@ function getBreakContext(state, label) {
|
|
|
6047
5205
|
/* c8 ignore next */
|
|
6048
5206
|
return null;
|
|
6049
5207
|
}
|
|
6050
|
-
/**
|
|
6051
|
-
* Gets a context for a `return` statement.
|
|
6052
|
-
*/
|
|
6053
5208
|
function getReturnContext(state) {
|
|
6054
5209
|
let context = state.tryContext;
|
|
6055
5210
|
while (context) {
|
|
@@ -6058,9 +5213,6 @@ function getReturnContext(state) {
|
|
|
6058
5213
|
}
|
|
6059
5214
|
return state;
|
|
6060
5215
|
}
|
|
6061
|
-
/**
|
|
6062
|
-
* Gets a context for a `throw` statement.
|
|
6063
|
-
*/
|
|
6064
5216
|
function getThrowContext(state) {
|
|
6065
5217
|
let context = state.tryContext;
|
|
6066
5218
|
while (context) {
|
|
@@ -6069,19 +5221,9 @@ function getThrowContext(state) {
|
|
|
6069
5221
|
}
|
|
6070
5222
|
return state;
|
|
6071
5223
|
}
|
|
6072
|
-
/**
|
|
6073
|
-
* Removes a given element from a given array.
|
|
6074
|
-
*/
|
|
6075
5224
|
function remove(xs, x) {
|
|
6076
5225
|
xs.splice(xs.indexOf(x), 1);
|
|
6077
5226
|
}
|
|
6078
|
-
/**
|
|
6079
|
-
* Disconnect given segments.
|
|
6080
|
-
*
|
|
6081
|
-
* This is used in a process for switch statements.
|
|
6082
|
-
* If there is the "default" chunk before other cases, the order is different
|
|
6083
|
-
* between node's and running's.
|
|
6084
|
-
*/
|
|
6085
5227
|
function removeConnection(prevSegments, nextSegments) {
|
|
6086
5228
|
for (let i = 0; i < prevSegments.length; ++i) {
|
|
6087
5229
|
const prevSegment = prevSegments[i];
|
|
@@ -6092,9 +5234,6 @@ function removeConnection(prevSegments, nextSegments) {
|
|
|
6092
5234
|
remove(nextSegment.allPrevSegments, prevSegment);
|
|
6093
5235
|
}
|
|
6094
5236
|
}
|
|
6095
|
-
/**
|
|
6096
|
-
* Creates looping path.
|
|
6097
|
-
*/
|
|
6098
5237
|
function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
|
|
6099
5238
|
const fromSegments = CodePathSegment.flattenUnusedSegments(unflattenedFromSegments);
|
|
6100
5239
|
const toSegments = CodePathSegment.flattenUnusedSegments(unflattenedToSegments);
|
|
@@ -6110,12 +5249,6 @@ function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
|
|
|
6110
5249
|
state.notifyLooped(fromSegment, toSegment);
|
|
6111
5250
|
}
|
|
6112
5251
|
}
|
|
6113
|
-
/**
|
|
6114
|
-
* Finalizes segments of `test` chunk of a ForStatement.
|
|
6115
|
-
*
|
|
6116
|
-
* - Adds `false` paths to paths which are leaving from the loop.
|
|
6117
|
-
* - Sets `true` paths to paths which go to the body.
|
|
6118
|
-
*/
|
|
6119
5252
|
function finalizeTestSegmentsOfFor(context, choiceContext, head) {
|
|
6120
5253
|
if (!choiceContext.processed) {
|
|
6121
5254
|
choiceContext.trueForkContext.add(head);
|
|
@@ -6125,9 +5258,6 @@ function finalizeTestSegmentsOfFor(context, choiceContext, head) {
|
|
|
6125
5258
|
if (context.test !== true) context.brokenForkContext.addAll(choiceContext.falseForkContext);
|
|
6126
5259
|
context.endOfTestSegments = choiceContext.trueForkContext.makeNext(0, -1);
|
|
6127
5260
|
}
|
|
6128
|
-
/**
|
|
6129
|
-
* A class which manages state to analyze code paths.
|
|
6130
|
-
*/
|
|
6131
5261
|
var CodePathState = class {
|
|
6132
5262
|
idGenerator;
|
|
6133
5263
|
notifyLooped;
|
|
@@ -6164,60 +5294,29 @@ var CodePathState = class {
|
|
|
6164
5294
|
returned.add = addToReturnedOrThrown.bind(null, returned, thrown, final);
|
|
6165
5295
|
thrown.add = addToReturnedOrThrown.bind(null, thrown, returned, final);
|
|
6166
5296
|
}
|
|
6167
|
-
/** The head segments. */
|
|
6168
5297
|
get headSegments() {
|
|
6169
5298
|
return this.forkContext.head;
|
|
6170
5299
|
}
|
|
6171
|
-
/**
|
|
6172
|
-
* The parent forking context.
|
|
6173
|
-
* This is used for the root of new forks.
|
|
6174
|
-
*/
|
|
6175
5300
|
get parentForkContext() {
|
|
6176
5301
|
const current = this.forkContext;
|
|
6177
5302
|
return current && current.upper;
|
|
6178
5303
|
}
|
|
6179
|
-
/**
|
|
6180
|
-
* Creates and stacks new forking context.
|
|
6181
|
-
*/
|
|
6182
5304
|
pushForkContext(forkLeavingPath) {
|
|
6183
5305
|
this.forkContext = ForkContext.newEmpty(this.forkContext, forkLeavingPath);
|
|
6184
5306
|
return this.forkContext;
|
|
6185
5307
|
}
|
|
6186
|
-
/**
|
|
6187
|
-
* Pops and merges the last forking context.
|
|
6188
|
-
*/
|
|
6189
5308
|
popForkContext() {
|
|
6190
5309
|
const lastContext = this.forkContext;
|
|
6191
5310
|
this.forkContext = lastContext.upper;
|
|
6192
5311
|
this.forkContext.replaceHead(lastContext.makeNext(0, -1));
|
|
6193
5312
|
return lastContext;
|
|
6194
5313
|
}
|
|
6195
|
-
/**
|
|
6196
|
-
* Creates a new path.
|
|
6197
|
-
*/
|
|
6198
5314
|
forkPath() {
|
|
6199
5315
|
this.forkContext.add(this.parentForkContext.makeNext(-1, -1));
|
|
6200
5316
|
}
|
|
6201
|
-
/**
|
|
6202
|
-
* Creates a bypass path.
|
|
6203
|
-
* This is used for such as IfStatement which does not have "else" chunk.
|
|
6204
|
-
*/
|
|
6205
5317
|
forkBypassPath() {
|
|
6206
5318
|
this.forkContext.add(this.parentForkContext.head);
|
|
6207
5319
|
}
|
|
6208
|
-
/**
|
|
6209
|
-
* Creates a context for ConditionalExpression, LogicalExpression,
|
|
6210
|
-
* AssignmentExpression (logical assignments only), IfStatement,
|
|
6211
|
-
* WhileStatement, DoWhileStatement, or ForStatement.
|
|
6212
|
-
*
|
|
6213
|
-
* @param kind A kind string.
|
|
6214
|
-
* If the new context is LogicalExpression's or AssignmentExpression's,
|
|
6215
|
-
* this is `"&&"` or `"||"` or `"??"`.
|
|
6216
|
-
* If it's IfStatement's or ConditionalExpression's, this is `"test"`.
|
|
6217
|
-
* Otherwise, this is `"loop"`.
|
|
6218
|
-
* @param isForkingAsResult A flag that shows that goes different
|
|
6219
|
-
* paths between `true` and `false`.
|
|
6220
|
-
*/
|
|
6221
5320
|
pushChoiceContext(kind, isForkingAsResult) {
|
|
6222
5321
|
this.choiceContext = {
|
|
6223
5322
|
upper: this.choiceContext,
|
|
@@ -6229,9 +5328,6 @@ var CodePathState = class {
|
|
|
6229
5328
|
processed: false
|
|
6230
5329
|
};
|
|
6231
5330
|
}
|
|
6232
|
-
/**
|
|
6233
|
-
* Pops the last choice context and finalizes it.
|
|
6234
|
-
*/
|
|
6235
5331
|
popChoiceContext() {
|
|
6236
5332
|
const context = this.choiceContext;
|
|
6237
5333
|
this.choiceContext = context.upper;
|
|
@@ -6272,10 +5368,6 @@ var CodePathState = class {
|
|
|
6272
5368
|
forkContext.replaceHead(prevForkContext.makeNext(0, -1));
|
|
6273
5369
|
return context;
|
|
6274
5370
|
}
|
|
6275
|
-
/**
|
|
6276
|
-
* Makes a code path segment of the right-hand operand of a logical
|
|
6277
|
-
* expression.
|
|
6278
|
-
*/
|
|
6279
5371
|
makeLogicalRight() {
|
|
6280
5372
|
const context = this.choiceContext;
|
|
6281
5373
|
const forkContext = this.forkContext;
|
|
@@ -6313,9 +5405,6 @@ var CodePathState = class {
|
|
|
6313
5405
|
forkContext.replaceHead(forkContext.makeNext(-1, -1));
|
|
6314
5406
|
}
|
|
6315
5407
|
}
|
|
6316
|
-
/**
|
|
6317
|
-
* Makes a code path segment of the `if` block.
|
|
6318
|
-
*/
|
|
6319
5408
|
makeIfConsequent() {
|
|
6320
5409
|
const context = this.choiceContext;
|
|
6321
5410
|
const forkContext = this.forkContext;
|
|
@@ -6327,9 +5416,6 @@ var CodePathState = class {
|
|
|
6327
5416
|
context.processed = false;
|
|
6328
5417
|
forkContext.replaceHead(context.trueForkContext.makeNext(0, -1));
|
|
6329
5418
|
}
|
|
6330
|
-
/**
|
|
6331
|
-
* Makes a code path segment of the `else` block.
|
|
6332
|
-
*/
|
|
6333
5419
|
makeIfAlternate() {
|
|
6334
5420
|
const context = this.choiceContext;
|
|
6335
5421
|
const forkContext = this.forkContext;
|
|
@@ -6338,50 +5424,26 @@ var CodePathState = class {
|
|
|
6338
5424
|
context.processed = true;
|
|
6339
5425
|
forkContext.replaceHead(context.falseForkContext.makeNext(0, -1));
|
|
6340
5426
|
}
|
|
6341
|
-
/**
|
|
6342
|
-
* Push a new `ChainExpression` context to the stack.
|
|
6343
|
-
* This method is called on entering to each `ChainExpression` node.
|
|
6344
|
-
* This context is used to count forking in the optional chain then merge
|
|
6345
|
-
* them on exiting from the `ChainExpression` node.
|
|
6346
|
-
*/
|
|
6347
5427
|
pushChainContext() {
|
|
6348
5428
|
this.chainContext = {
|
|
6349
5429
|
upper: this.chainContext,
|
|
6350
5430
|
countChoiceContexts: 0
|
|
6351
5431
|
};
|
|
6352
5432
|
}
|
|
6353
|
-
/**
|
|
6354
|
-
* Pop a `ChainExpression` context from the stack.
|
|
6355
|
-
* This method is called on exiting from each `ChainExpression` node.
|
|
6356
|
-
* This merges all forks of the last optional chaining.
|
|
6357
|
-
*/
|
|
6358
5433
|
popChainContext() {
|
|
6359
5434
|
const context = this.chainContext;
|
|
6360
5435
|
this.chainContext = context.upper;
|
|
6361
5436
|
for (let i = context.countChoiceContexts; i > 0; --i) this.popChoiceContext();
|
|
6362
5437
|
}
|
|
6363
|
-
/**
|
|
6364
|
-
* Create a choice context for optional access.
|
|
6365
|
-
* This method is called on entering to each `(Call|Member)Expression[optional=true]` node.
|
|
6366
|
-
* This creates a choice context as similar to `LogicalExpression[operator="??"]` node.
|
|
6367
|
-
*/
|
|
6368
5438
|
makeOptionalNode() {
|
|
6369
5439
|
if (this.chainContext) {
|
|
6370
5440
|
this.chainContext.countChoiceContexts += 1;
|
|
6371
5441
|
this.pushChoiceContext("??", false);
|
|
6372
5442
|
}
|
|
6373
5443
|
}
|
|
6374
|
-
/**
|
|
6375
|
-
* Create a fork.
|
|
6376
|
-
* This method is called on entering to the `arguments|property` property
|
|
6377
|
-
* of each `(Call|Member)Expression` node.
|
|
6378
|
-
*/
|
|
6379
5444
|
makeOptionalRight() {
|
|
6380
5445
|
if (this.chainContext) this.makeLogicalRight();
|
|
6381
5446
|
}
|
|
6382
|
-
/**
|
|
6383
|
-
* Creates a context object of SwitchStatement and stacks it.
|
|
6384
|
-
*/
|
|
6385
5447
|
pushSwitchContext(hasCase, label) {
|
|
6386
5448
|
this.switchContext = {
|
|
6387
5449
|
upper: this.switchContext,
|
|
@@ -6394,14 +5456,6 @@ var CodePathState = class {
|
|
|
6394
5456
|
};
|
|
6395
5457
|
this.pushBreakContext(true, label);
|
|
6396
5458
|
}
|
|
6397
|
-
/**
|
|
6398
|
-
* Pops the last context of SwitchStatement and finalizes it.
|
|
6399
|
-
*
|
|
6400
|
-
* - Disposes all forking stack for `case` and `default`.
|
|
6401
|
-
* - Creates the next code path segment from `context.brokenForkContext`.
|
|
6402
|
-
* - If the last `SwitchCase` node is not a `default` part, creates a path
|
|
6403
|
-
* to the `default` body.
|
|
6404
|
-
*/
|
|
6405
5459
|
popSwitchContext() {
|
|
6406
5460
|
const context = this.switchContext;
|
|
6407
5461
|
this.switchContext = context.upper;
|
|
@@ -6427,9 +5481,6 @@ var CodePathState = class {
|
|
|
6427
5481
|
for (let i = 0; i < context.countForks; ++i) this.forkContext = this.forkContext.upper;
|
|
6428
5482
|
this.forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
|
|
6429
5483
|
}
|
|
6430
|
-
/**
|
|
6431
|
-
* Makes a code path segment for a `SwitchCase` node.
|
|
6432
|
-
*/
|
|
6433
5484
|
makeSwitchCaseBody(isEmpty, isDefault) {
|
|
6434
5485
|
const context = this.switchContext;
|
|
6435
5486
|
if (!context.hasCase) return;
|
|
@@ -6447,9 +5498,6 @@ var CodePathState = class {
|
|
|
6447
5498
|
context.lastIsDefault = isDefault;
|
|
6448
5499
|
context.countForks += 1;
|
|
6449
5500
|
}
|
|
6450
|
-
/**
|
|
6451
|
-
* Creates a context object of TryStatement and stacks it.
|
|
6452
|
-
*/
|
|
6453
5501
|
pushTryContext(hasFinalizer) {
|
|
6454
5502
|
this.tryContext = {
|
|
6455
5503
|
upper: this.tryContext,
|
|
@@ -6461,9 +5509,6 @@ var CodePathState = class {
|
|
|
6461
5509
|
lastOfCatchIsReachable: false
|
|
6462
5510
|
};
|
|
6463
5511
|
}
|
|
6464
|
-
/**
|
|
6465
|
-
* Pops the last context of TryStatement and finalizes it.
|
|
6466
|
-
*/
|
|
6467
5512
|
popTryContext() {
|
|
6468
5513
|
const context = this.tryContext;
|
|
6469
5514
|
this.tryContext = context.upper;
|
|
@@ -6483,9 +5528,6 @@ var CodePathState = class {
|
|
|
6483
5528
|
this.forkContext.replaceHead(normalSegments);
|
|
6484
5529
|
if (!context.lastOfTryIsReachable && !context.lastOfCatchIsReachable) this.forkContext.makeUnreachable(-1, -1);
|
|
6485
5530
|
}
|
|
6486
|
-
/**
|
|
6487
|
-
* Makes a code path segment for a `catch` block.
|
|
6488
|
-
*/
|
|
6489
5531
|
makeCatchBlock() {
|
|
6490
5532
|
const context = this.tryContext;
|
|
6491
5533
|
const forkContext = this.forkContext;
|
|
@@ -6499,13 +5541,6 @@ var CodePathState = class {
|
|
|
6499
5541
|
this.forkBypassPath();
|
|
6500
5542
|
this.forkContext.add(thrownSegments);
|
|
6501
5543
|
}
|
|
6502
|
-
/**
|
|
6503
|
-
* Makes a code path segment for a `finally` block.
|
|
6504
|
-
*
|
|
6505
|
-
* In the `finally` block, parallel paths are created. The parallel paths
|
|
6506
|
-
* are used as leaving-paths. The leaving-paths are paths from `return`
|
|
6507
|
-
* statements and `throw` statements in a `try` block or a `catch` block.
|
|
6508
|
-
*/
|
|
6509
5544
|
makeFinallyBlock() {
|
|
6510
5545
|
const context = this.tryContext;
|
|
6511
5546
|
let forkContext = this.forkContext;
|
|
@@ -6529,10 +5564,6 @@ var CodePathState = class {
|
|
|
6529
5564
|
this.pushForkContext(true);
|
|
6530
5565
|
this.forkContext.add(segments);
|
|
6531
5566
|
}
|
|
6532
|
-
/**
|
|
6533
|
-
* Makes a code path segment from the first throwable node to the `catch`
|
|
6534
|
-
* block or the `finally` block.
|
|
6535
|
-
*/
|
|
6536
5567
|
makeFirstThrowablePathInTryBlock() {
|
|
6537
5568
|
const forkContext = this.forkContext;
|
|
6538
5569
|
if (!forkContext.reachable) return;
|
|
@@ -6541,9 +5572,6 @@ var CodePathState = class {
|
|
|
6541
5572
|
context.thrownForkContext.add(forkContext.head);
|
|
6542
5573
|
forkContext.replaceHead(forkContext.makeNext(-1, -1));
|
|
6543
5574
|
}
|
|
6544
|
-
/**
|
|
6545
|
-
* Creates a context object of a loop statement and stacks it.
|
|
6546
|
-
*/
|
|
6547
5575
|
pushLoopContext(type, label) {
|
|
6548
5576
|
const forkContext = this.forkContext;
|
|
6549
5577
|
const breakContext = this.pushBreakContext(true, label);
|
|
@@ -6603,9 +5631,6 @@ var CodePathState = class {
|
|
|
6603
5631
|
default: throw new Error(`unknown type: "${type}"`);
|
|
6604
5632
|
}
|
|
6605
5633
|
}
|
|
6606
|
-
/**
|
|
6607
|
-
* Pops the last context of a loop statement and finalizes it.
|
|
6608
|
-
*/
|
|
6609
5634
|
popLoopContext() {
|
|
6610
5635
|
const context = this.loopContext;
|
|
6611
5636
|
this.loopContext = context.upper;
|
|
@@ -6638,9 +5663,6 @@ var CodePathState = class {
|
|
|
6638
5663
|
if (brokenForkContext.empty) forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
6639
5664
|
else forkContext.replaceHead(brokenForkContext.makeNext(0, -1));
|
|
6640
5665
|
}
|
|
6641
|
-
/**
|
|
6642
|
-
* Makes a code path segment for the test part of a WhileStatement.
|
|
6643
|
-
*/
|
|
6644
5666
|
makeWhileTest(test) {
|
|
6645
5667
|
const context = this.loopContext;
|
|
6646
5668
|
const forkContext = this.forkContext;
|
|
@@ -6649,9 +5671,6 @@ var CodePathState = class {
|
|
|
6649
5671
|
context.continueDestSegments = testSegments;
|
|
6650
5672
|
forkContext.replaceHead(testSegments);
|
|
6651
5673
|
}
|
|
6652
|
-
/**
|
|
6653
|
-
* Makes a code path segment for the body part of a WhileStatement.
|
|
6654
|
-
*/
|
|
6655
5674
|
makeWhileBody() {
|
|
6656
5675
|
const context = this.loopContext;
|
|
6657
5676
|
const choiceContext = this.choiceContext;
|
|
@@ -6663,9 +5682,6 @@ var CodePathState = class {
|
|
|
6663
5682
|
if (context.test !== true) context.brokenForkContext.addAll(choiceContext.falseForkContext);
|
|
6664
5683
|
forkContext.replaceHead(choiceContext.trueForkContext.makeNext(0, -1));
|
|
6665
5684
|
}
|
|
6666
|
-
/**
|
|
6667
|
-
* Makes a code path segment for the body part of a DoWhileStatement.
|
|
6668
|
-
*/
|
|
6669
5685
|
makeDoWhileBody() {
|
|
6670
5686
|
const context = this.loopContext;
|
|
6671
5687
|
const forkContext = this.forkContext;
|
|
@@ -6673,9 +5689,6 @@ var CodePathState = class {
|
|
|
6673
5689
|
context.entrySegments = bodySegments;
|
|
6674
5690
|
forkContext.replaceHead(bodySegments);
|
|
6675
5691
|
}
|
|
6676
|
-
/**
|
|
6677
|
-
* Makes a code path segment for the test part of a DoWhileStatement.
|
|
6678
|
-
*/
|
|
6679
5692
|
makeDoWhileTest(test) {
|
|
6680
5693
|
const context = this.loopContext;
|
|
6681
5694
|
const forkContext = this.forkContext;
|
|
@@ -6686,9 +5699,6 @@ var CodePathState = class {
|
|
|
6686
5699
|
forkContext.replaceHead(testSegments);
|
|
6687
5700
|
}
|
|
6688
5701
|
}
|
|
6689
|
-
/**
|
|
6690
|
-
* Makes a code path segment for the test part of a ForStatement.
|
|
6691
|
-
*/
|
|
6692
5702
|
makeForTest(test) {
|
|
6693
5703
|
const context = this.loopContext;
|
|
6694
5704
|
const forkContext = this.forkContext;
|
|
@@ -6699,9 +5709,6 @@ var CodePathState = class {
|
|
|
6699
5709
|
context.continueDestSegments = context.testSegments = testSegments;
|
|
6700
5710
|
forkContext.replaceHead(testSegments);
|
|
6701
5711
|
}
|
|
6702
|
-
/**
|
|
6703
|
-
* Makes a code path segment for the update part of a ForStatement.
|
|
6704
|
-
*/
|
|
6705
5712
|
makeForUpdate() {
|
|
6706
5713
|
const context = this.loopContext;
|
|
6707
5714
|
const choiceContext = this.choiceContext;
|
|
@@ -6712,9 +5719,6 @@ var CodePathState = class {
|
|
|
6712
5719
|
context.continueDestSegments = context.updateSegments = updateSegments;
|
|
6713
5720
|
forkContext.replaceHead(updateSegments);
|
|
6714
5721
|
}
|
|
6715
|
-
/**
|
|
6716
|
-
* Makes a code path segment for the body part of a ForStatement.
|
|
6717
|
-
*/
|
|
6718
5722
|
makeForBody() {
|
|
6719
5723
|
const context = this.loopContext;
|
|
6720
5724
|
const choiceContext = this.choiceContext;
|
|
@@ -6734,10 +5738,6 @@ var CodePathState = class {
|
|
|
6734
5738
|
context.continueDestSegments = context.continueDestSegments || bodySegments;
|
|
6735
5739
|
forkContext.replaceHead(bodySegments);
|
|
6736
5740
|
}
|
|
6737
|
-
/**
|
|
6738
|
-
* Makes a code path segment for the left part of a ForInStatement and a
|
|
6739
|
-
* ForOfStatement.
|
|
6740
|
-
*/
|
|
6741
5741
|
makeForInOfLeft() {
|
|
6742
5742
|
const context = this.loopContext;
|
|
6743
5743
|
const forkContext = this.forkContext;
|
|
@@ -6746,10 +5746,6 @@ var CodePathState = class {
|
|
|
6746
5746
|
context.leftSegments = context.continueDestSegments = leftSegments;
|
|
6747
5747
|
forkContext.replaceHead(leftSegments);
|
|
6748
5748
|
}
|
|
6749
|
-
/**
|
|
6750
|
-
* Makes a code path segment for the right part of a ForInStatement and a
|
|
6751
|
-
* ForOfStatement.
|
|
6752
|
-
*/
|
|
6753
5749
|
makeForInOfRight() {
|
|
6754
5750
|
const context = this.loopContext;
|
|
6755
5751
|
const forkContext = this.forkContext;
|
|
@@ -6759,10 +5755,6 @@ var CodePathState = class {
|
|
|
6759
5755
|
context.endOfLeftSegments = forkContext.head;
|
|
6760
5756
|
forkContext.replaceHead(rightSegments);
|
|
6761
5757
|
}
|
|
6762
|
-
/**
|
|
6763
|
-
* Makes a code path segment for the body part of a ForInStatement and a
|
|
6764
|
-
* ForOfStatement.
|
|
6765
|
-
*/
|
|
6766
5758
|
makeForInOfBody() {
|
|
6767
5759
|
const context = this.loopContext;
|
|
6768
5760
|
const forkContext = this.forkContext;
|
|
@@ -6773,9 +5765,6 @@ var CodePathState = class {
|
|
|
6773
5765
|
context.brokenForkContext.add(forkContext.head);
|
|
6774
5766
|
forkContext.replaceHead(bodySegments);
|
|
6775
5767
|
}
|
|
6776
|
-
/**
|
|
6777
|
-
* Creates new context for BreakStatement.
|
|
6778
|
-
*/
|
|
6779
5768
|
pushBreakContext(breakable, label) {
|
|
6780
5769
|
this.breakContext = {
|
|
6781
5770
|
upper: this.breakContext,
|
|
@@ -6785,9 +5774,6 @@ var CodePathState = class {
|
|
|
6785
5774
|
};
|
|
6786
5775
|
return this.breakContext;
|
|
6787
5776
|
}
|
|
6788
|
-
/**
|
|
6789
|
-
* Removes the top item of the break context stack.
|
|
6790
|
-
*/
|
|
6791
5777
|
popBreakContext() {
|
|
6792
5778
|
const context = this.breakContext;
|
|
6793
5779
|
const forkContext = this.forkContext;
|
|
@@ -6801,12 +5787,6 @@ var CodePathState = class {
|
|
|
6801
5787
|
}
|
|
6802
5788
|
return context;
|
|
6803
5789
|
}
|
|
6804
|
-
/**
|
|
6805
|
-
* Makes a path for a `break` statement.
|
|
6806
|
-
*
|
|
6807
|
-
* It registers the head segment to a context of `break`.
|
|
6808
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
6809
|
-
*/
|
|
6810
5790
|
makeBreak(label) {
|
|
6811
5791
|
const forkContext = this.forkContext;
|
|
6812
5792
|
if (!forkContext.reachable) return;
|
|
@@ -6815,12 +5795,6 @@ var CodePathState = class {
|
|
|
6815
5795
|
/* c8 ignore next */
|
|
6816
5796
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
6817
5797
|
}
|
|
6818
|
-
/**
|
|
6819
|
-
* Makes a path for a `continue` statement.
|
|
6820
|
-
*
|
|
6821
|
-
* It makes a looping path.
|
|
6822
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
6823
|
-
*/
|
|
6824
5798
|
makeContinue(label) {
|
|
6825
5799
|
const forkContext = this.forkContext;
|
|
6826
5800
|
if (!forkContext.reachable) return;
|
|
@@ -6833,12 +5807,6 @@ var CodePathState = class {
|
|
|
6833
5807
|
}
|
|
6834
5808
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
6835
5809
|
}
|
|
6836
|
-
/**
|
|
6837
|
-
* Makes a path for a `return` statement.
|
|
6838
|
-
*
|
|
6839
|
-
* It registers the head segment to a context of `return`.
|
|
6840
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
6841
|
-
*/
|
|
6842
5810
|
makeReturn() {
|
|
6843
5811
|
const forkContext = this.forkContext;
|
|
6844
5812
|
if (forkContext.reachable) {
|
|
@@ -6846,12 +5814,6 @@ var CodePathState = class {
|
|
|
6846
5814
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
6847
5815
|
}
|
|
6848
5816
|
}
|
|
6849
|
-
/**
|
|
6850
|
-
* Makes a path for a `throw` statement.
|
|
6851
|
-
*
|
|
6852
|
-
* It registers the head segment to a context of `throw`.
|
|
6853
|
-
* It makes new unreachable segment, then it set the head with the segment.
|
|
6854
|
-
*/
|
|
6855
5817
|
makeThrow() {
|
|
6856
5818
|
const forkContext = this.forkContext;
|
|
6857
5819
|
if (forkContext.reachable) {
|
|
@@ -6859,17 +5821,11 @@ var CodePathState = class {
|
|
|
6859
5821
|
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
|
|
6860
5822
|
}
|
|
6861
5823
|
}
|
|
6862
|
-
/**
|
|
6863
|
-
* Makes the final path.
|
|
6864
|
-
*/
|
|
6865
5824
|
makeFinal() {
|
|
6866
5825
|
const segments = this.currentSegments;
|
|
6867
5826
|
if (segments.length > 0 && segments[0].reachable) this.returnedForkContext.add(segments);
|
|
6868
5827
|
}
|
|
6869
5828
|
};
|
|
6870
|
-
/**
|
|
6871
|
-
* A generator for unique ids.
|
|
6872
|
-
*/
|
|
6873
5829
|
var IdGenerator = class {
|
|
6874
5830
|
prefix;
|
|
6875
5831
|
n;
|
|
@@ -6877,9 +5833,6 @@ var IdGenerator = class {
|
|
|
6877
5833
|
this.prefix = String(prefix);
|
|
6878
5834
|
this.n = 0;
|
|
6879
5835
|
}
|
|
6880
|
-
/**
|
|
6881
|
-
* Generates id.
|
|
6882
|
-
*/
|
|
6883
5836
|
next() {
|
|
6884
5837
|
this.n = 1 + this.n | 0;
|
|
6885
5838
|
/* c8 ignore start */
|
|
@@ -6887,22 +5840,11 @@ var IdGenerator = class {
|
|
|
6887
5840
|
return this.prefix + this.n;
|
|
6888
5841
|
}
|
|
6889
5842
|
};
|
|
6890
|
-
/**
|
|
6891
|
-
* A code path.
|
|
6892
|
-
*/
|
|
6893
5843
|
var CodePath = class {
|
|
6894
|
-
/** The identifier of this code path. */
|
|
6895
5844
|
id;
|
|
6896
|
-
/**
|
|
6897
|
-
* The reason that this code path was started. May be "program",
|
|
6898
|
-
* "function", "class-field-initializer", or "class-static-block".
|
|
6899
|
-
*/
|
|
6900
5845
|
origin;
|
|
6901
|
-
/** The code path of the upper function scope. */
|
|
6902
5846
|
upper;
|
|
6903
|
-
/** The code paths of nested function scopes. */
|
|
6904
5847
|
childCodePaths;
|
|
6905
|
-
/** Internal state for the code path. */
|
|
6906
5848
|
internal;
|
|
6907
5849
|
constructor({ id, origin, upper, onLooped }) {
|
|
6908
5850
|
this.id = id;
|
|
@@ -6912,48 +5854,24 @@ var CodePath = class {
|
|
|
6912
5854
|
this.internal = new CodePathState(new IdGenerator(`${id}_`), onLooped);
|
|
6913
5855
|
if (upper) upper.childCodePaths.push(this);
|
|
6914
5856
|
}
|
|
6915
|
-
/**
|
|
6916
|
-
* Gets the state of a given code path.
|
|
6917
|
-
*/
|
|
6918
5857
|
static getState(codePath) {
|
|
6919
5858
|
return codePath.internal;
|
|
6920
5859
|
}
|
|
6921
|
-
/** The initial code path segment. */
|
|
6922
5860
|
get initialSegment() {
|
|
6923
5861
|
return this.internal.initialSegment;
|
|
6924
5862
|
}
|
|
6925
|
-
/**
|
|
6926
|
-
* Final code path segments.
|
|
6927
|
-
* This array is a mix of `returnedSegments` and `thrownSegments`.
|
|
6928
|
-
*/
|
|
6929
5863
|
get finalSegments() {
|
|
6930
5864
|
return this.internal.finalSegments;
|
|
6931
5865
|
}
|
|
6932
|
-
/**
|
|
6933
|
-
* Final code path segments which is with `return` statements.
|
|
6934
|
-
* This array contains the last path segment if it's reachable.
|
|
6935
|
-
* Since the reachable last path returns `undefined`.
|
|
6936
|
-
*/
|
|
6937
5866
|
get returnedSegments() {
|
|
6938
5867
|
return this.internal.returnedForkContext;
|
|
6939
5868
|
}
|
|
6940
|
-
/** Final code path segments which is with `throw` statements. */
|
|
6941
5869
|
get thrownSegments() {
|
|
6942
5870
|
return this.internal.thrownForkContext;
|
|
6943
5871
|
}
|
|
6944
|
-
/** Current code path segments. */
|
|
6945
5872
|
get currentSegments() {
|
|
6946
5873
|
return this.internal.currentSegments;
|
|
6947
5874
|
}
|
|
6948
|
-
/**
|
|
6949
|
-
* Traverses all segments in this code path.
|
|
6950
|
-
*
|
|
6951
|
-
* This method enumerates segments in order from the head.
|
|
6952
|
-
*
|
|
6953
|
-
* The `controller` object has two methods:
|
|
6954
|
-
* - `controller.skip()` - Skip the following segments in this branch.
|
|
6955
|
-
* - `controller.break()` - Skip all following segments.
|
|
6956
|
-
*/
|
|
6957
5875
|
traverseSegments(options, callback) {
|
|
6958
5876
|
let resolvedOptions;
|
|
6959
5877
|
let resolvedCallback;
|
|
@@ -7019,45 +5937,23 @@ var CodePath = class {
|
|
|
7019
5937
|
}
|
|
7020
5938
|
};
|
|
7021
5939
|
const breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/u;
|
|
7022
|
-
/**
|
|
7023
|
-
* Checks whether or not a given node is a `case` node (not `default` node).
|
|
7024
|
-
*/
|
|
7025
5940
|
function isCaseNode(node) {
|
|
7026
5941
|
return Boolean(node.test);
|
|
7027
5942
|
}
|
|
7028
|
-
/**
|
|
7029
|
-
* Checks if a given node appears as the value of a PropertyDefinition node.
|
|
7030
|
-
*/
|
|
7031
5943
|
function isPropertyDefinitionValue(node) {
|
|
7032
5944
|
const parent = node.parent;
|
|
7033
5945
|
return parent && parent.type === "PropertyDefinition" && parent.value === node;
|
|
7034
5946
|
}
|
|
7035
|
-
/**
|
|
7036
|
-
* Checks whether the given logical operator is taken into account for the code
|
|
7037
|
-
* path analysis.
|
|
7038
|
-
*/
|
|
7039
5947
|
function isHandledLogicalOperator(operator) {
|
|
7040
5948
|
return operator === "&&" || operator === "||" || operator === "??";
|
|
7041
5949
|
}
|
|
7042
|
-
/**
|
|
7043
|
-
* Checks whether the given assignment operator is a logical assignment operator.
|
|
7044
|
-
* Logical assignments are taken into account for the code path analysis
|
|
7045
|
-
* because of their short-circuiting semantics.
|
|
7046
|
-
*/
|
|
7047
5950
|
function isLogicalAssignmentOperator(operator) {
|
|
7048
5951
|
return operator === "&&=" || operator === "||=" || operator === "??=";
|
|
7049
5952
|
}
|
|
7050
|
-
/**
|
|
7051
|
-
* Gets the label if the parent node of a given node is a LabeledStatement.
|
|
7052
|
-
*/
|
|
7053
5953
|
function getLabel(node) {
|
|
7054
5954
|
if (node.parent.type === "LabeledStatement") return node.parent.label.name;
|
|
7055
5955
|
return null;
|
|
7056
5956
|
}
|
|
7057
|
-
/**
|
|
7058
|
-
* Checks whether or not a given logical expression node goes different path
|
|
7059
|
-
* between the `true` case and the `false` case.
|
|
7060
|
-
*/
|
|
7061
5957
|
function isForkingByTrueOrFalse(node) {
|
|
7062
5958
|
const parent = node.parent;
|
|
7063
5959
|
switch (parent.type) {
|
|
@@ -7071,21 +5967,9 @@ function isForkingByTrueOrFalse(node) {
|
|
|
7071
5967
|
default: return false;
|
|
7072
5968
|
}
|
|
7073
5969
|
}
|
|
7074
|
-
/**
|
|
7075
|
-
* Gets the boolean value of a given literal node.
|
|
7076
|
-
*
|
|
7077
|
-
* This is used to detect infinity loops (ex: `while (true) {}`).
|
|
7078
|
-
* Statements preceded by an infinity loop are unreachable if the loop didn't
|
|
7079
|
-
* have any `break` statement.
|
|
7080
|
-
*/
|
|
7081
5970
|
function getBooleanValueIfSimpleConstant(node) {
|
|
7082
5971
|
if (node.type === "Literal") return Boolean(node.value);
|
|
7083
5972
|
}
|
|
7084
|
-
/**
|
|
7085
|
-
* Checks that a given identifier node is a reference or not.
|
|
7086
|
-
*
|
|
7087
|
-
* This is used to detect the first throwable node in a `try` block.
|
|
7088
|
-
*/
|
|
7089
5973
|
function isIdentifierReference(node) {
|
|
7090
5974
|
const parent = node.parent;
|
|
7091
5975
|
switch (parent.type) {
|
|
@@ -7113,15 +5997,6 @@ function isIdentifierReference(node) {
|
|
|
7113
5997
|
default: return true;
|
|
7114
5998
|
}
|
|
7115
5999
|
}
|
|
7116
|
-
/**
|
|
7117
|
-
* Updates the current segment with the head segment.
|
|
7118
|
-
* This is similar to local branches and tracking branches of git.
|
|
7119
|
-
*
|
|
7120
|
-
* To separate the current and the head is in order to not make useless segments.
|
|
7121
|
-
*
|
|
7122
|
-
* In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
|
|
7123
|
-
* events are fired.
|
|
7124
|
-
*/
|
|
7125
6000
|
function forwardCurrentToHead(analyzer, node) {
|
|
7126
6001
|
const codePath = analyzer.codePath;
|
|
7127
6002
|
const state = CodePath.getState(codePath);
|
|
@@ -7148,10 +6023,6 @@ function forwardCurrentToHead(analyzer, node) {
|
|
|
7148
6023
|
}
|
|
7149
6024
|
}
|
|
7150
6025
|
}
|
|
7151
|
-
/**
|
|
7152
|
-
* Updates the current segment with empty.
|
|
7153
|
-
* This is called at the last of functions or the program.
|
|
7154
|
-
*/
|
|
7155
6026
|
function leaveFromCurrentSegment(analyzer, node) {
|
|
7156
6027
|
const state = CodePath.getState(analyzer.codePath);
|
|
7157
6028
|
const currentSegments = state.currentSegments;
|
|
@@ -7161,13 +6032,6 @@ function leaveFromCurrentSegment(analyzer, node) {
|
|
|
7161
6032
|
}
|
|
7162
6033
|
state.currentSegments = [];
|
|
7163
6034
|
}
|
|
7164
|
-
/**
|
|
7165
|
-
* Updates the code path due to the position of a given node in the parent node
|
|
7166
|
-
* thereof.
|
|
7167
|
-
*
|
|
7168
|
-
* For example, if the node is `parent.consequent`, this creates a fork from the
|
|
7169
|
-
* current path.
|
|
7170
|
-
*/
|
|
7171
6035
|
function preprocess(analyzer, node) {
|
|
7172
6036
|
const codePath = analyzer.codePath;
|
|
7173
6037
|
const state = CodePath.getState(codePath);
|
|
@@ -7232,17 +6096,10 @@ function preprocess(analyzer, node) {
|
|
|
7232
6096
|
}
|
|
7233
6097
|
}
|
|
7234
6098
|
}
|
|
7235
|
-
/**
|
|
7236
|
-
* Updates the code path due to the type of a given node in entering.
|
|
7237
|
-
*/
|
|
7238
6099
|
function processCodePathToEnter(analyzer, node) {
|
|
7239
6100
|
let codePath = analyzer.codePath;
|
|
7240
6101
|
let state = codePath && CodePath.getState(codePath);
|
|
7241
6102
|
const parent = node.parent;
|
|
7242
|
-
/**
|
|
7243
|
-
* Creates a new code path and trigger the onCodePathStart event
|
|
7244
|
-
* based on the currently selected node.
|
|
7245
|
-
*/
|
|
7246
6103
|
function startCodePath(origin) {
|
|
7247
6104
|
if (codePath) forwardCurrentToHead(analyzer, node);
|
|
7248
6105
|
codePath = analyzer.codePath = new CodePath({
|
|
@@ -7308,9 +6165,6 @@ function processCodePathToEnter(analyzer, node) {
|
|
|
7308
6165
|
}
|
|
7309
6166
|
forwardCurrentToHead(analyzer, node);
|
|
7310
6167
|
}
|
|
7311
|
-
/**
|
|
7312
|
-
* Updates the code path due to the type of a given node in leaving.
|
|
7313
|
-
*/
|
|
7314
6168
|
function processCodePathToExit(analyzer, node) {
|
|
7315
6169
|
const codePath = analyzer.codePath;
|
|
7316
6170
|
const state = CodePath.getState(codePath);
|
|
@@ -7386,9 +6240,6 @@ function processCodePathToExit(analyzer, node) {
|
|
|
7386
6240
|
}
|
|
7387
6241
|
if (!dontForward) forwardCurrentToHead(analyzer, node);
|
|
7388
6242
|
}
|
|
7389
|
-
/**
|
|
7390
|
-
* Updates the code path to finalize the current code path.
|
|
7391
|
-
*/
|
|
7392
6243
|
function postprocess(analyzer, node) {
|
|
7393
6244
|
function endCodePath() {
|
|
7394
6245
|
const codePath = analyzer.codePath;
|
|
@@ -7411,10 +6262,6 @@ function postprocess(analyzer, node) {
|
|
|
7411
6262
|
}
|
|
7412
6263
|
if (isPropertyDefinitionValue(node)) endCodePath();
|
|
7413
6264
|
}
|
|
7414
|
-
/**
|
|
7415
|
-
* The class to analyze code paths.
|
|
7416
|
-
* This class implements the EventGenerator interface.
|
|
7417
|
-
*/
|
|
7418
6265
|
var CodePathAnalyzer = class {
|
|
7419
6266
|
emitter;
|
|
7420
6267
|
codePath;
|
|
@@ -7430,30 +6277,18 @@ var CodePathAnalyzer = class {
|
|
|
7430
6277
|
this.currentNode = null;
|
|
7431
6278
|
this.onLooped = this._onLooped.bind(this);
|
|
7432
6279
|
}
|
|
7433
|
-
/**
|
|
7434
|
-
* Does the process to enter a given AST node.
|
|
7435
|
-
* This updates state of analysis and calls `enterNode` of the wrapped.
|
|
7436
|
-
*/
|
|
7437
6280
|
enterNode(node) {
|
|
7438
6281
|
this.currentNode = node;
|
|
7439
6282
|
if (node.parent) preprocess(this, node);
|
|
7440
6283
|
processCodePathToEnter(this, node);
|
|
7441
6284
|
this.currentNode = null;
|
|
7442
6285
|
}
|
|
7443
|
-
/**
|
|
7444
|
-
* Does the process to leave a given AST node.
|
|
7445
|
-
* This updates state of analysis and calls `leaveNode` of the wrapped.
|
|
7446
|
-
*/
|
|
7447
6286
|
leaveNode(node) {
|
|
7448
6287
|
this.currentNode = node;
|
|
7449
6288
|
processCodePathToExit(this, node);
|
|
7450
6289
|
postprocess(this, node);
|
|
7451
6290
|
this.currentNode = null;
|
|
7452
6291
|
}
|
|
7453
|
-
/**
|
|
7454
|
-
* This is called on a code path looped.
|
|
7455
|
-
* Then this raises a looped event.
|
|
7456
|
-
*/
|
|
7457
6292
|
_onLooped(fromSegment, toSegment) {
|
|
7458
6293
|
if (fromSegment.reachable && toSegment.reachable) this.emitter.emit("onCodePathSegmentLoop", fromSegment, toSegment, this.currentNode);
|
|
7459
6294
|
}
|
|
@@ -7461,17 +6296,9 @@ var CodePathAnalyzer = class {
|
|
|
7461
6296
|
|
|
7462
6297
|
//#endregion
|
|
7463
6298
|
//#region src/rules/rules-of-hooks/rules-of-hooks.ts
|
|
7464
|
-
/**
|
|
7465
|
-
* Catch all identifiers that begin with "use" followed by an uppercase Latin
|
|
7466
|
-
* character to exclude identifiers like "user".
|
|
7467
|
-
*/
|
|
7468
6299
|
function isHookName(s) {
|
|
7469
6300
|
return s === "use" || /^use[A-Z0-9]/.test(s);
|
|
7470
6301
|
}
|
|
7471
|
-
/**
|
|
7472
|
-
* We consider hooks to be a hook name identifier or a member expression
|
|
7473
|
-
* containing a hook name.
|
|
7474
|
-
*/
|
|
7475
6302
|
function isHook(node) {
|
|
7476
6303
|
const expr = Extract.unwrap(node);
|
|
7477
6304
|
if (expr.type === "Identifier") return isHookName(expr.name);
|
|
@@ -7480,27 +6307,15 @@ function isHook(node) {
|
|
|
7480
6307
|
return obj.type === "Identifier" && /^[A-Z].*/.test(obj.name);
|
|
7481
6308
|
} else return false;
|
|
7482
6309
|
}
|
|
7483
|
-
/**
|
|
7484
|
-
* Checks if the node is a React component name. React component names must
|
|
7485
|
-
* always start with an uppercase letter.
|
|
7486
|
-
*/
|
|
7487
6310
|
function isComponentName(node) {
|
|
7488
6311
|
return node.type === "Identifier" && /^[A-Z]/.test(node.name);
|
|
7489
6312
|
}
|
|
7490
6313
|
function isReactFunction(node, functionName) {
|
|
7491
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;
|
|
7492
6315
|
}
|
|
7493
|
-
/**
|
|
7494
|
-
* Checks if the node is a callback argument of forwardRef. This render function
|
|
7495
|
-
* should follow the rules of hooks.
|
|
7496
|
-
*/
|
|
7497
6316
|
function isForwardRefCallback(node) {
|
|
7498
6317
|
return !!(node.parent && "callee" in node.parent && node.parent.callee && isReactFunction(node.parent.callee, "forwardRef"));
|
|
7499
6318
|
}
|
|
7500
|
-
/**
|
|
7501
|
-
* Checks if the node is a callback argument of React.memo. This anonymous
|
|
7502
|
-
* functional component should follow the rules of hooks.
|
|
7503
|
-
*/
|
|
7504
6319
|
function isMemoCallback(node) {
|
|
7505
6320
|
return !!(node.parent && "callee" in node.parent && node.parent.callee && isReactFunction(node.parent.callee, "memo"));
|
|
7506
6321
|
}
|
|
@@ -7579,17 +6394,11 @@ const rule = {
|
|
|
7579
6394
|
}
|
|
7580
6395
|
}
|
|
7581
6396
|
}
|
|
7582
|
-
/**
|
|
7583
|
-
* SourceCode that also works down to ESLint 3.0.0
|
|
7584
|
-
*/
|
|
7585
6397
|
const getSourceCode = typeof context.getSourceCode === "function" ? () => {
|
|
7586
6398
|
return context.getSourceCode();
|
|
7587
6399
|
} : () => {
|
|
7588
6400
|
return context.sourceCode;
|
|
7589
6401
|
};
|
|
7590
|
-
/**
|
|
7591
|
-
* SourceCode#getScope that also works down to ESLint 3.0.0
|
|
7592
|
-
*/
|
|
7593
6402
|
const getScope = typeof context.getScope === "function" ? () => {
|
|
7594
6403
|
return context.getScope();
|
|
7595
6404
|
} : (node) => {
|
|
@@ -7609,27 +6418,6 @@ const rule = {
|
|
|
7609
6418
|
if (reactHooksMap?.size === 0) return;
|
|
7610
6419
|
else if (typeof reactHooksMap === "undefined") throw new Error("Unexpected undefined reactHooksMap");
|
|
7611
6420
|
const cyclic = /* @__PURE__ */ new Set();
|
|
7612
|
-
/**
|
|
7613
|
-
* Count the number of code paths from the start of the function to this
|
|
7614
|
-
* segment. For example:
|
|
7615
|
-
*
|
|
7616
|
-
* ```js
|
|
7617
|
-
* function MyComponent() {
|
|
7618
|
-
* if (condition) {
|
|
7619
|
-
* // Segment 1
|
|
7620
|
-
* } else {
|
|
7621
|
-
* // Segment 2
|
|
7622
|
-
* }
|
|
7623
|
-
* // Segment 3
|
|
7624
|
-
* }
|
|
7625
|
-
* ```
|
|
7626
|
-
*
|
|
7627
|
-
* Segments 1 and 2 have one path to the beginning of `MyComponent` and
|
|
7628
|
-
* segment 3 has two paths to the beginning of `MyComponent` since we
|
|
7629
|
-
* could have either taken the path of segment 1 or segment 2.
|
|
7630
|
-
*
|
|
7631
|
-
* Populates `cyclic` with cyclic segments.
|
|
7632
|
-
*/
|
|
7633
6421
|
function countPathsFromStart(segment, pathHistory) {
|
|
7634
6422
|
const { cache } = countPathsFromStart;
|
|
7635
6423
|
let paths = cache.get(segment.id);
|
|
@@ -7652,27 +6440,6 @@ const rule = {
|
|
|
7652
6440
|
else cache.set(segment.id, paths);
|
|
7653
6441
|
return paths;
|
|
7654
6442
|
}
|
|
7655
|
-
/**
|
|
7656
|
-
* Count the number of code paths from this segment to the end of the
|
|
7657
|
-
* function. For example:
|
|
7658
|
-
*
|
|
7659
|
-
* ```js
|
|
7660
|
-
* function MyComponent() {
|
|
7661
|
-
* // Segment 1
|
|
7662
|
-
* if (condition) {
|
|
7663
|
-
* // Segment 2
|
|
7664
|
-
* } else {
|
|
7665
|
-
* // Segment 3
|
|
7666
|
-
* }
|
|
7667
|
-
* }
|
|
7668
|
-
* ```
|
|
7669
|
-
*
|
|
7670
|
-
* Segments 2 and 3 have one path to the end of `MyComponent` and
|
|
7671
|
-
* segment 1 has two paths to the end of `MyComponent` since we could
|
|
7672
|
-
* either take the path of segment 1 or segment 2.
|
|
7673
|
-
*
|
|
7674
|
-
* Populates `cyclic` with cyclic segments.
|
|
7675
|
-
*/
|
|
7676
6443
|
function countPathsToEnd(segment, pathHistory) {
|
|
7677
6444
|
const { cache } = countPathsToEnd;
|
|
7678
6445
|
let paths = cache.get(segment.id);
|
|
@@ -7694,27 +6461,6 @@ const rule = {
|
|
|
7694
6461
|
cache.set(segment.id, paths);
|
|
7695
6462
|
return paths;
|
|
7696
6463
|
}
|
|
7697
|
-
/**
|
|
7698
|
-
* Gets the shortest path length to the start of a code path.
|
|
7699
|
-
* For example:
|
|
7700
|
-
*
|
|
7701
|
-
* ```js
|
|
7702
|
-
* function MyComponent() {
|
|
7703
|
-
* if (condition) {
|
|
7704
|
-
* // Segment 1
|
|
7705
|
-
* }
|
|
7706
|
-
* // Segment 2
|
|
7707
|
-
* }
|
|
7708
|
-
* ```
|
|
7709
|
-
*
|
|
7710
|
-
* There is only one path from segment 1 to the code path start. Its
|
|
7711
|
-
* length is one so that is the shortest path.
|
|
7712
|
-
*
|
|
7713
|
-
* There are two paths from segment 2 to the code path start. One
|
|
7714
|
-
* through segment 1 with a length of two and another directly to the
|
|
7715
|
-
* start with a length of one. The shortest path has a length of one
|
|
7716
|
-
* so we would return that.
|
|
7717
|
-
*/
|
|
7718
6464
|
function shortestPathLengthToStart(segment) {
|
|
7719
6465
|
const { cache } = shortestPathLengthToStart;
|
|
7720
6466
|
let length = cache.get(segment.id);
|
|
@@ -7858,13 +6604,6 @@ const rule = {
|
|
|
7858
6604
|
};
|
|
7859
6605
|
}
|
|
7860
6606
|
};
|
|
7861
|
-
/**
|
|
7862
|
-
* Gets the static name of a function AST node. For function declarations it is
|
|
7863
|
-
* easy. For anonymous function expressions it is much harder. If you search for
|
|
7864
|
-
* `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
|
|
7865
|
-
* where JS gives anonymous function expressions names. We roughly detect the
|
|
7866
|
-
* same AST nodes with some exceptions to better fit our use case.
|
|
7867
|
-
*/
|
|
7868
6607
|
function getFunctionName(node) {
|
|
7869
6608
|
if (node.type === "ComponentDeclaration" || node.type === "HookDeclaration" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" && node.id) return node.id;
|
|
7870
6609
|
else if (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
@@ -7875,9 +6614,6 @@ function getFunctionName(node) {
|
|
|
7875
6614
|
else return;
|
|
7876
6615
|
} else return;
|
|
7877
6616
|
}
|
|
7878
|
-
/**
|
|
7879
|
-
* Convenience function for peeking the last item in a stack.
|
|
7880
|
-
*/
|
|
7881
6617
|
function last(array) {
|
|
7882
6618
|
return array[array.length - 1];
|
|
7883
6619
|
}
|
|
@@ -7920,11 +6656,6 @@ const IDENTIFIER_VISITOR_KEYS = {
|
|
|
7920
6656
|
[AST_NODE_TYPES.UpdateExpression]: ["argument"],
|
|
7921
6657
|
[AST_NODE_TYPES.YieldExpression]: ["argument"]
|
|
7922
6658
|
};
|
|
7923
|
-
/**
|
|
7924
|
-
* Get all nested identifiers in a expression like node
|
|
7925
|
-
* @param node The node to get the nested identifiers from
|
|
7926
|
-
* @returns All nested identifiers
|
|
7927
|
-
*/
|
|
7928
6659
|
function getNestedIdentifiers(node) {
|
|
7929
6660
|
const identifiers = [];
|
|
7930
6661
|
simpleTraverse(node, {
|
|
@@ -7953,23 +6684,10 @@ function isTerminatingStatement(node) {
|
|
|
7953
6684
|
if (node.type === AST_NODE_TYPES.BlockStatement) return node.body.some(isTerminatingStatement);
|
|
7954
6685
|
return false;
|
|
7955
6686
|
}
|
|
7956
|
-
/**
|
|
7957
|
-
* Get the display name of a setState call reference for reporting.
|
|
7958
|
-
* @param node The setState call node (CallExpression or Identifier)
|
|
7959
|
-
* @param getText The source text getter
|
|
7960
|
-
* @returns The fully qualified name of the call
|
|
7961
|
-
*/
|
|
7962
6687
|
function getCallName(node, getText) {
|
|
7963
6688
|
if (node.type === AST_NODE_TYPES.CallExpression) return Extract.getFullyQualifiedName(node.callee, getText);
|
|
7964
6689
|
return Extract.getFullyQualifiedName(node, getText);
|
|
7965
6690
|
}
|
|
7966
|
-
/**
|
|
7967
|
-
* Get the actual CallExpression node from a setState call reference.
|
|
7968
|
-
* When the node is an Identifier that is the callee of a CallExpression,
|
|
7969
|
-
* returns the parent CallExpression; otherwise returns the node itself.
|
|
7970
|
-
* @param node The setState call node (CallExpression or Identifier)
|
|
7971
|
-
* @returns The actual CallExpression node
|
|
7972
|
-
*/
|
|
7973
6691
|
function getSetStateCallExpression(node) {
|
|
7974
6692
|
return Check.isIdentifier(node) && node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node ? node.parent : node;
|
|
7975
6693
|
}
|
|
@@ -8057,13 +6775,6 @@ function createFactCollector$3(additionalStateHooks, additionalEffectHooks) {
|
|
|
8057
6775
|
|
|
8058
6776
|
//#endregion
|
|
8059
6777
|
//#region src/rules/set-state-in-effect/origins.ts
|
|
8060
|
-
/**
|
|
8061
|
-
* Create a resolver that answers whether an identifier or call originates from
|
|
8062
|
-
* a `useState`-like hook's setter function (the element at index 1 of the
|
|
8063
|
-
* returned tuple).
|
|
8064
|
-
* @param context The rule context
|
|
8065
|
-
* @returns The setState provenance resolver
|
|
8066
|
-
*/
|
|
8067
6778
|
function createSetStateResolver$1(context) {
|
|
8068
6779
|
const { additionalStateHooks } = getSettingsFromContext(context);
|
|
8069
6780
|
function isUseStateCall(node) {
|
|
@@ -8075,7 +6786,7 @@ function createSetStateResolver$1(context) {
|
|
|
8075
6786
|
if (initNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
8076
6787
|
if (!isUseStateCall(initNode)) return false;
|
|
8077
6788
|
const variableNodeParent = initNode.parent;
|
|
8078
|
-
if (!("id"
|
|
6789
|
+
if (!hasProperty(variableNodeParent, "id") || variableNodeParent.id?.type !== AST_NODE_TYPES.ArrayPattern) return true;
|
|
8079
6790
|
return variableNodeParent.id.elements.findIndex((e) => e != null && Check.isIdentifier(e, id.name)) === at;
|
|
8080
6791
|
}
|
|
8081
6792
|
function isSetStateCall(node) {
|
|
@@ -8084,7 +6795,7 @@ function createSetStateResolver$1(context) {
|
|
|
8084
6795
|
case AST_NODE_TYPES.CallExpression: {
|
|
8085
6796
|
const innerCallee = Extract.unwrap(callee.callee);
|
|
8086
6797
|
if (innerCallee.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
8087
|
-
if (!("name"
|
|
6798
|
+
if (!hasProperty(innerCallee.object, "name")) return false;
|
|
8088
6799
|
const isAt = Extract.getCalleeName(callee) === "at";
|
|
8089
6800
|
const [index] = callee.arguments;
|
|
8090
6801
|
if (!isAt || index == null) return false;
|
|
@@ -8093,7 +6804,7 @@ function createSetStateResolver$1(context) {
|
|
|
8093
6804
|
}
|
|
8094
6805
|
case AST_NODE_TYPES.Identifier: return isSetStateId(callee, 1);
|
|
8095
6806
|
case AST_NODE_TYPES.MemberExpression: {
|
|
8096
|
-
if (!("name"
|
|
6807
|
+
if (!hasProperty(callee.object, "name")) return false;
|
|
8097
6808
|
const property = callee.property;
|
|
8098
6809
|
const propertyScope = context.sourceCode.getScope(node);
|
|
8099
6810
|
return getStaticValue(property, propertyScope)?.value === 1 && isSetStateId(callee.object, 1);
|
|
@@ -8141,12 +6852,6 @@ function isInitializedFromRef(context, name, initialScope, seen = /* @__PURE__ *
|
|
|
8141
6852
|
}
|
|
8142
6853
|
return false;
|
|
8143
6854
|
}
|
|
8144
|
-
/**
|
|
8145
|
-
* Check if the setState call is using a ref value, which is safe to use in an effect (ex: `setState(ref.current.scrollTop)`)
|
|
8146
|
-
* @param context The rule context
|
|
8147
|
-
* @param node The setState call argument to check
|
|
8148
|
-
* @returns `true` if the argument value is derived from a ref
|
|
8149
|
-
*/
|
|
8150
6855
|
function isArgumentUsingRefValue(context, node) {
|
|
8151
6856
|
const isUsingRefValue = (n) => {
|
|
8152
6857
|
switch (n.type) {
|
|
@@ -8172,13 +6877,6 @@ function isArgumentUsingRefValue(context, node) {
|
|
|
8172
6877
|
if (isUsingRefValue(node)) return true;
|
|
8173
6878
|
return Check.isFunction(node) && context.sourceCode.getScope(node.body).references.some((r) => isUsingRefValue(r.identifier));
|
|
8174
6879
|
}
|
|
8175
|
-
/**
|
|
8176
|
-
* Check if a setState call is inside a conditional block whose test expression
|
|
8177
|
-
* is derived from a ref value (e.g. `if (prevRef.current !== value) setState(...)`).
|
|
8178
|
-
* @param context The ESLint rule context
|
|
8179
|
-
* @param node The AST node to check
|
|
8180
|
-
* @returns `true` if the node is inside a ref-gated conditional block
|
|
8181
|
-
*/
|
|
8182
6880
|
function isRefGatedContext(context, node) {
|
|
8183
6881
|
let child = node;
|
|
8184
6882
|
let current = node.parent;
|
|
@@ -8206,11 +6904,6 @@ function isRefGatedContext(context, node) {
|
|
|
8206
6904
|
function isRefInExpression(context, node) {
|
|
8207
6905
|
return getNestedIdentifiers(node).some((id) => isInitializedFromRef(context, id.name, context.sourceCode.getScope(id)));
|
|
8208
6906
|
}
|
|
8209
|
-
/**
|
|
8210
|
-
* Get the name that identifies the source of a `.current` member access.
|
|
8211
|
-
* @param node The object of the member expression
|
|
8212
|
-
* @returns The identifier name, the last property name of a member chain, or null
|
|
8213
|
-
*/
|
|
8214
6907
|
function getRefSourceName(node) {
|
|
8215
6908
|
switch (node.type) {
|
|
8216
6909
|
case AST_NODE_TYPES.Identifier: return node.name;
|
|
@@ -8224,15 +6917,6 @@ function getRefSourceName(node) {
|
|
|
8224
6917
|
|
|
8225
6918
|
//#endregion
|
|
8226
6919
|
//#region src/rules/set-state-in-effect/effects.ts
|
|
8227
|
-
/**
|
|
8228
|
-
* Infer the setState calls that run synchronously during an effect setup from
|
|
8229
|
-
* the collected facts: calls made directly in the setup body (or an IIFE within
|
|
8230
|
-
* it), and calls reached indirectly through local functions, hook callbacks, or
|
|
8231
|
-
* setup identifiers passed to the effect.
|
|
8232
|
-
* @param context The rule context
|
|
8233
|
-
* @param facts The collected facts
|
|
8234
|
-
* @returns The violations to report, in report order
|
|
8235
|
-
*/
|
|
8236
6920
|
function inferViolations$2(context, facts) {
|
|
8237
6921
|
const { additionalEffectHooks } = getSettingsFromContext(context);
|
|
8238
6922
|
const resolver = createSetStateResolver$1(context);
|
|
@@ -8453,13 +7137,6 @@ function createFactCollector$2(additionalStateHooks) {
|
|
|
8453
7137
|
|
|
8454
7138
|
//#endregion
|
|
8455
7139
|
//#region src/rules/set-state-in-render/origins.ts
|
|
8456
|
-
/**
|
|
8457
|
-
* Create a resolver that answers whether an identifier or call originates from
|
|
8458
|
-
* a `useState`-like hook's setter function (the element at index 1 of the
|
|
8459
|
-
* returned tuple).
|
|
8460
|
-
* @param context The rule context
|
|
8461
|
-
* @returns The setState provenance resolver
|
|
8462
|
-
*/
|
|
8463
7140
|
function createSetStateResolver(context) {
|
|
8464
7141
|
const { additionalStateHooks } = getSettingsFromContext(context);
|
|
8465
7142
|
function isUseStateCall(node) {
|
|
@@ -8471,7 +7148,7 @@ function createSetStateResolver(context) {
|
|
|
8471
7148
|
if (initNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
8472
7149
|
if (!isUseStateCall(initNode)) return false;
|
|
8473
7150
|
const variableNodeParent = initNode.parent;
|
|
8474
|
-
if (!("id"
|
|
7151
|
+
if (!hasProperty(variableNodeParent, "id") || variableNodeParent.id?.type !== AST_NODE_TYPES.ArrayPattern) return true;
|
|
8475
7152
|
return variableNodeParent.id.elements.findIndex((e) => e != null && Check.isIdentifier(e, id.name)) === at;
|
|
8476
7153
|
}
|
|
8477
7154
|
function isSetStateCall(node) {
|
|
@@ -8480,7 +7157,7 @@ function createSetStateResolver(context) {
|
|
|
8480
7157
|
case AST_NODE_TYPES.CallExpression: {
|
|
8481
7158
|
const innerCallee = Extract.unwrap(callee.callee);
|
|
8482
7159
|
if (innerCallee.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
8483
|
-
if (!("name"
|
|
7160
|
+
if (!hasProperty(innerCallee.object, "name")) return false;
|
|
8484
7161
|
const isAt = Extract.getCalleeName(callee) === "at";
|
|
8485
7162
|
const [index] = callee.arguments;
|
|
8486
7163
|
if (!isAt || index == null) return false;
|
|
@@ -8489,7 +7166,7 @@ function createSetStateResolver(context) {
|
|
|
8489
7166
|
}
|
|
8490
7167
|
case AST_NODE_TYPES.Identifier: return isSetStateId(callee, 1);
|
|
8491
7168
|
case AST_NODE_TYPES.MemberExpression: {
|
|
8492
|
-
if (!("name"
|
|
7169
|
+
if (!hasProperty(callee.object, "name")) return false;
|
|
8493
7170
|
const property = callee.property;
|
|
8494
7171
|
const propertyScope = context.sourceCode.getScope(node);
|
|
8495
7172
|
return getStaticValue(property, propertyScope)?.value === 1 && isSetStateId(callee.object, 1);
|
|
@@ -8506,14 +7183,6 @@ function createSetStateResolver(context) {
|
|
|
8506
7183
|
|
|
8507
7184
|
//#endregion
|
|
8508
7185
|
//#region src/rules/set-state-in-render/effects.ts
|
|
8509
|
-
/**
|
|
8510
|
-
* Infer the setState calls that run unconditionally during render from the
|
|
8511
|
-
* collected facts: calls made directly in a component function body that are
|
|
8512
|
-
* neither guarded by a conditional nor positioned after an early return.
|
|
8513
|
-
* @param context The rule context
|
|
8514
|
-
* @param facts The collected facts
|
|
8515
|
-
* @returns The violations to report, in report order
|
|
8516
|
-
*/
|
|
8517
7186
|
function inferViolations$1(context, facts) {
|
|
8518
7187
|
const resolver = createSetStateResolver(context);
|
|
8519
7188
|
const getText = (n) => context.sourceCode.getText(n);
|
|
@@ -8580,10 +7249,6 @@ function createFactCollector$1() {
|
|
|
8580
7249
|
|
|
8581
7250
|
//#endregion
|
|
8582
7251
|
//#region src/rules/static-components/helpers.ts
|
|
8583
|
-
/**
|
|
8584
|
-
* Expression kinds that always produce a brand-new value on every evaluation, and are
|
|
8585
|
-
* therefore considered "dynamically created" when used as a component's value.
|
|
8586
|
-
*/
|
|
8587
7252
|
const KNOWN_DYNAMIC_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
8588
7253
|
AST_NODE_TYPES.ArrowFunctionExpression,
|
|
8589
7254
|
AST_NODE_TYPES.CallExpression,
|
|
@@ -8591,10 +7256,6 @@ const KNOWN_DYNAMIC_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
|
8591
7256
|
AST_NODE_TYPES.FunctionExpression,
|
|
8592
7257
|
AST_NODE_TYPES.NewExpression
|
|
8593
7258
|
]);
|
|
8594
|
-
/**
|
|
8595
|
-
* Builds a predicate that tells whether a given node lies within the body of one of the
|
|
8596
|
-
* collected function or class components, i.e. whether it is "created during render".
|
|
8597
|
-
*/
|
|
8598
7259
|
function createRenderBoundaryChecker(componentNodes) {
|
|
8599
7260
|
const components = new Set(componentNodes);
|
|
8600
7261
|
return (node) => Traverse.findParent(node, (n) => (Check.isFunction(n) || Check.isClass(n)) && components.has(n)) != null;
|
|
@@ -8602,11 +7263,6 @@ function createRenderBoundaryChecker(componentNodes) {
|
|
|
8602
7263
|
|
|
8603
7264
|
//#endregion
|
|
8604
7265
|
//#region src/rules/static-components/origins.ts
|
|
8605
|
-
/**
|
|
8606
|
-
* Resolves an expression down to the node that dynamically created its value, if any.
|
|
8607
|
-
* Recurses through identifier references, both branches of ternaries and logical
|
|
8608
|
-
* expressions, and the final element of sequence expressions.
|
|
8609
|
-
*/
|
|
8610
7266
|
function findDynamicCreationSite(context, node, isInsideRender, seen) {
|
|
8611
7267
|
const expr = Extract.unwrap(node);
|
|
8612
7268
|
if (KNOWN_DYNAMIC_EXPRESSION_TYPES.has(expr.type)) return expr;
|
|
@@ -8626,10 +7282,6 @@ function findDynamicCreationSite(context, node, isInsideRender, seen) {
|
|
|
8626
7282
|
default: return null;
|
|
8627
7283
|
}
|
|
8628
7284
|
}
|
|
8629
|
-
/**
|
|
8630
|
-
* Looks for a reassignment of `variable` (e.g. `Component = createComponent();`) whose
|
|
8631
|
-
* right-hand side is dynamically created.
|
|
8632
|
-
*/
|
|
8633
7285
|
function findReassignmentCreationSite(context, variable, isInsideRender, seen) {
|
|
8634
7286
|
for (const ref of variable.references) {
|
|
8635
7287
|
if (!ref.isWrite()) continue;
|
|
@@ -8640,10 +7292,6 @@ function findReassignmentCreationSite(context, variable, isInsideRender, seen) {
|
|
|
8640
7292
|
}
|
|
8641
7293
|
return null;
|
|
8642
7294
|
}
|
|
8643
|
-
/**
|
|
8644
|
-
* Resolves a component binding to the node that dynamically created its value, if the
|
|
8645
|
-
* value is created during render (a fresh function/class/call result on every render).
|
|
8646
|
-
*/
|
|
8647
7295
|
function resolveDynamicComponentOrigin(context, variable, isInsideRender, seen = /* @__PURE__ */ new Set()) {
|
|
8648
7296
|
if (seen.has(variable)) return {
|
|
8649
7297
|
creationNode: null,
|
|
@@ -8677,11 +7325,6 @@ function resolveDynamicComponentOrigin(context, variable, isInsideRender, seen =
|
|
|
8677
7325
|
|
|
8678
7326
|
//#endregion
|
|
8679
7327
|
//#region src/rules/static-components/effects.ts
|
|
8680
|
-
/**
|
|
8681
|
-
* Turn collected JSX component usages into effects: a usage is an effect when the
|
|
8682
|
-
* binding it refers to is declared inside a component's render body and its value is
|
|
8683
|
-
* dynamically created, i.e. the component is recreated on every render.
|
|
8684
|
-
*/
|
|
8685
7328
|
function inferCreatedComponents(context, componentUsages, isInsideRender) {
|
|
8686
7329
|
const effects = [];
|
|
8687
7330
|
for (const usage of componentUsages) {
|
|
@@ -8822,22 +7465,11 @@ function create$2(context) {
|
|
|
8822
7465
|
|
|
8823
7466
|
//#endregion
|
|
8824
7467
|
//#region src/rules/use-memo/helpers.ts
|
|
8825
|
-
/**
|
|
8826
|
-
* Check whether the result of a `useMemo` call is used (assigned, returned,
|
|
8827
|
-
* passed as an argument, etc.) rather than discarded as a bare statement.
|
|
8828
|
-
* @param node The `useMemo` call expression
|
|
8829
|
-
* @returns `true` when the call result flows into a surrounding expression
|
|
8830
|
-
*/
|
|
8831
7468
|
function isResultUsed(node) {
|
|
8832
7469
|
let parent = node.parent;
|
|
8833
7470
|
while (Check.isTypeExpression(parent)) parent = parent.parent;
|
|
8834
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;
|
|
8835
7472
|
}
|
|
8836
|
-
/**
|
|
8837
|
-
* Gets the nested return statements in the node that are within the same function
|
|
8838
|
-
* @param node The AST node
|
|
8839
|
-
* @returns The nested return statements in the node
|
|
8840
|
-
*/
|
|
8841
7473
|
function getNestedReturnStatements(node) {
|
|
8842
7474
|
const statements = [];
|
|
8843
7475
|
const boundary = Check.isFunction(node) ? node : Traverse.findParent(node, Check.isFunction);
|
|
@@ -8906,15 +7538,6 @@ function isDeclaredInsideCallback(variable, callback) {
|
|
|
8906
7538
|
}
|
|
8907
7539
|
return false;
|
|
8908
7540
|
}
|
|
8909
|
-
/**
|
|
8910
|
-
* Resolve whether an identifier written inside a `useMemo` callback refers to a
|
|
8911
|
-
* variable declared outside the callback. Unresolved identifiers and variables
|
|
8912
|
-
* without definitions are treated as outer variables.
|
|
8913
|
-
* @param context The rule context
|
|
8914
|
-
* @param target The written identifier
|
|
8915
|
-
* @param callback The `useMemo` callback the write occurs in
|
|
8916
|
-
* @returns `true` when the write reassigns an outer variable
|
|
8917
|
-
*/
|
|
8918
7541
|
function isOuterVariable(context, target, callback) {
|
|
8919
7542
|
const scope = context.sourceCode.getScope(target);
|
|
8920
7543
|
const variable = findVariable(scope, target);
|
|
@@ -8924,14 +7547,6 @@ function isOuterVariable(context, target, callback) {
|
|
|
8924
7547
|
|
|
8925
7548
|
//#endregion
|
|
8926
7549
|
//#region src/rules/use-memo/effects.ts
|
|
8927
|
-
/**
|
|
8928
|
-
* Infer the `useMemo` usage violations from the collected facts: unused
|
|
8929
|
-
* results, callbacks with parameters, async or generator callbacks, outer
|
|
8930
|
-
* variable reassignments, and callbacks that never return a value.
|
|
8931
|
-
* @param context The rule context
|
|
8932
|
-
* @param facts The collected facts
|
|
8933
|
-
* @returns The violations to report, in report order
|
|
8934
|
-
*/
|
|
8935
7550
|
function inferViolations(context, facts) {
|
|
8936
7551
|
const violations = [];
|
|
8937
7552
|
for (const fact of facts.calls) {
|
|
@@ -9064,12 +7679,6 @@ const EXPRESSION_VISITOR_KEYS = {
|
|
|
9064
7679
|
[AST_NODE_TYPES.WhileStatement]: ["test"],
|
|
9065
7680
|
[AST_NODE_TYPES.YieldExpression]: ["argument"]
|
|
9066
7681
|
};
|
|
9067
|
-
/**
|
|
9068
|
-
* Get all nested expressions of type T in an expression like node
|
|
9069
|
-
* @param type The type of the expression to retrieve within the node
|
|
9070
|
-
* @returns A partially applied function bound to a predicate of type AST. The returned function can be called passing a
|
|
9071
|
-
* node, and it will return an array of all nested expressions of type AST.
|
|
9072
|
-
*/
|
|
9073
7682
|
function getNestedExpressionsOfType(type) {
|
|
9074
7683
|
const isNodeOfType = Check.is(type);
|
|
9075
7684
|
const recurse = (node) => {
|
|
@@ -9085,17 +7694,7 @@ function getNestedExpressionsOfType(type) {
|
|
|
9085
7694
|
};
|
|
9086
7695
|
return recurse;
|
|
9087
7696
|
}
|
|
9088
|
-
/**
|
|
9089
|
-
* Get all nested new expressions in an expression like node
|
|
9090
|
-
* @param node The node to get the nested new expressions from
|
|
9091
|
-
* @returns All nested new expressions
|
|
9092
|
-
*/
|
|
9093
7697
|
const getNestedNewExpressions = getNestedExpressionsOfType(AST_NODE_TYPES.NewExpression);
|
|
9094
|
-
/**
|
|
9095
|
-
* Get all nested call expressions in a expression like node
|
|
9096
|
-
* @param node The node to get the nested call expressions from
|
|
9097
|
-
* @returns All nested call expressions
|
|
9098
|
-
*/
|
|
9099
7698
|
const getNestedCallExpressions = getNestedExpressionsOfType(AST_NODE_TYPES.CallExpression);
|
|
9100
7699
|
|
|
9101
7700
|
//#endregion
|
|
@@ -9148,7 +7747,7 @@ function create(context) {
|
|
|
9148
7747
|
const [useStateInput] = node.arguments;
|
|
9149
7748
|
if (useStateInput != null) {
|
|
9150
7749
|
for (const expr of getNestedNewExpressions(useStateInput)) {
|
|
9151
|
-
if (!("name"
|
|
7750
|
+
if (!hasProperty(expr.callee, "name")) continue;
|
|
9152
7751
|
if (LAZY_INIT_ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
9153
7752
|
if (Traverse.findParent(expr, (n) => core.isUseCall(context, n)) != null) continue;
|
|
9154
7753
|
context.report({
|
|
@@ -9157,7 +7756,7 @@ function create(context) {
|
|
|
9157
7756
|
});
|
|
9158
7757
|
}
|
|
9159
7758
|
for (const expr of getNestedCallExpressions(useStateInput)) {
|
|
9160
|
-
if (!("name"
|
|
7759
|
+
if (!hasProperty(expr.callee, "name")) continue;
|
|
9161
7760
|
if (core.isHookName(expr.callee.name)) continue;
|
|
9162
7761
|
if (LAZY_INIT_ALLOW_LIST.includes(expr.callee.name)) continue;
|
|
9163
7762
|
if (Traverse.findParent(expr, (n) => core.isUseCall(context, n)) != null) continue;
|
|
@@ -9429,45 +8028,15 @@ function createConfig(base) {
|
|
|
9429
8028
|
const finalPlugin = {
|
|
9430
8029
|
...plugin,
|
|
9431
8030
|
configs: {
|
|
9432
|
-
/**
|
|
9433
|
-
* Disable rules in `eslint-plugin-react` that conflict with rules in this plugin
|
|
9434
|
-
*/
|
|
9435
8031
|
["disable-conflict-eslint-plugin-react"]: createConfig(disable_conflict_eslint_plugin_react_exports),
|
|
9436
|
-
/**
|
|
9437
|
-
* Disable rules in `eslint-plugin-react-hooks` that conflict with rules in this plugin
|
|
9438
|
-
*/
|
|
9439
8032
|
["disable-conflict-eslint-plugin-react-hooks"]: createConfig(disable_conflict_eslint_plugin_react_hooks_exports),
|
|
9440
|
-
/**
|
|
9441
|
-
* Disable experimental rules that might be subject to change in the future
|
|
9442
|
-
*/
|
|
9443
8033
|
["disable-experimental"]: createConfig(disable_experimental_exports),
|
|
9444
|
-
/**
|
|
9445
|
-
* Disable rules that can be enforced by TypeScript
|
|
9446
|
-
*/
|
|
9447
8034
|
["disable-type-checked"]: createConfig(disable_type_checked_exports),
|
|
9448
|
-
/**
|
|
9449
|
-
* Enforce rules that are recommended by ESLint React for general purpose React + React DOM projects
|
|
9450
|
-
*/
|
|
9451
8035
|
["recommended"]: createConfig(recommended_exports),
|
|
9452
|
-
/**
|
|
9453
|
-
* Same as the `recommended-typescript` preset but enables additional rules that require type information
|
|
9454
|
-
*/
|
|
9455
8036
|
["recommended-type-checked"]: createConfig(recommended_type_checked_exports),
|
|
9456
|
-
/**
|
|
9457
|
-
* Same as the `recommended` preset but disables rules that can be enforced by TypeScript
|
|
9458
|
-
*/
|
|
9459
8037
|
["recommended-typescript"]: createConfig(recommended_typescript_exports),
|
|
9460
|
-
/**
|
|
9461
|
-
* More strict version of the `recommended` preset
|
|
9462
|
-
*/
|
|
9463
8038
|
["strict"]: createConfig(strict_exports),
|
|
9464
|
-
/**
|
|
9465
|
-
* Same as the `strict-typescript` preset but enables additional rules that require type information
|
|
9466
|
-
*/
|
|
9467
8039
|
["strict-type-checked"]: createConfig(strict_type_checked_exports),
|
|
9468
|
-
/**
|
|
9469
|
-
* Same as the `strict` preset but disables rules that can be enforced by TypeScript
|
|
9470
|
-
*/
|
|
9471
8040
|
["strict-typescript"]: createConfig(strict_typescript_exports)
|
|
9472
8041
|
}
|
|
9473
8042
|
};
|