oxlint-plugin-react-doctor 0.7.7-dev.79abd71 → 0.7.7-dev.89dbf08
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 +370 -978
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,13 +7,6 @@ import { analyze } from "eslint-scope";
|
|
|
7
7
|
//#region src/plugin/utils/is-node-of-type.ts
|
|
8
8
|
const isNodeOfType = (node, type) => node !== null && typeof node === "object" && "type" in node && node.type === type;
|
|
9
9
|
//#endregion
|
|
10
|
-
//#region src/plugin/utils/is-type-only-import.ts
|
|
11
|
-
const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
|
|
12
|
-
if (!specifiers || specifiers.length === 0) return false;
|
|
13
|
-
return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
|
|
14
|
-
};
|
|
15
|
-
const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
|
|
16
|
-
//#endregion
|
|
17
10
|
//#region src/plugin/utils/non-react-jsx-dialect.ts
|
|
18
11
|
const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
|
|
19
12
|
"solid-js",
|
|
@@ -28,33 +21,17 @@ const NON_REACT_JSX_DIALECT_PACKAGES = new Set([
|
|
|
28
21
|
"vidode"
|
|
29
22
|
]);
|
|
30
23
|
const NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES = ["solid-js", "@builder.io/qwik"];
|
|
31
|
-
const REACT_JSX_DIALECT_PACKAGE_PREFIXES = [
|
|
32
|
-
"react",
|
|
33
|
-
"react-dom",
|
|
34
|
-
"preact"
|
|
35
|
-
];
|
|
36
24
|
const startsWithAny = (source, prefixes) => prefixes.some((prefix) => source === prefix || source.startsWith(`${prefix}/`));
|
|
37
|
-
const
|
|
38
|
-
let hasNonReactRuntime = false;
|
|
39
|
-
let hasReactRuntime = false;
|
|
25
|
+
const fileImportsNonReactJsxDialect = (program) => {
|
|
40
26
|
for (const statement of program.body) {
|
|
41
27
|
if (!isNodeOfType(statement, "ImportDeclaration")) continue;
|
|
42
|
-
const
|
|
43
|
-
if (isTypeOnlyImport(importDeclaration)) continue;
|
|
44
|
-
const source = importDeclaration.source;
|
|
28
|
+
const source = statement.source;
|
|
45
29
|
const value = source && typeof source.value === "string" ? source.value : null;
|
|
46
30
|
if (!value) continue;
|
|
47
|
-
if (NON_REACT_JSX_DIALECT_PACKAGES.has(value)
|
|
48
|
-
if (startsWithAny(value,
|
|
31
|
+
if (NON_REACT_JSX_DIALECT_PACKAGES.has(value)) return true;
|
|
32
|
+
if (startsWithAny(value, NON_REACT_JSX_DIALECT_PACKAGE_PREFIXES)) return true;
|
|
49
33
|
}
|
|
50
|
-
return
|
|
51
|
-
hasNonReactRuntime,
|
|
52
|
-
hasReactRuntime
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
const fileImportsNonReactJsxDialect = (program) => {
|
|
56
|
-
const runtimeImports = collectJsxRuntimeImports(program);
|
|
57
|
-
return runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
|
|
34
|
+
return false;
|
|
58
35
|
};
|
|
59
36
|
const jsxAttributeIsNonReactDialectMarker = (openingNode) => {
|
|
60
37
|
for (const attribute of openingNode.attributes) {
|
|
@@ -267,7 +244,6 @@ const VISITOR_NODE_NAME_PATTERN = /^[A-Z]/;
|
|
|
267
244
|
const wrapCreateForReactJsxOnly = (create) => ((context) => {
|
|
268
245
|
const innerVisitors = create(context);
|
|
269
246
|
let fileIsNonReactJsx = false;
|
|
270
|
-
let fileImportsReactRuntime = false;
|
|
271
247
|
const wrappedVisitors = {};
|
|
272
248
|
for (const [key, visitor] of Object.entries(innerVisitors)) {
|
|
273
249
|
if (typeof visitor !== "function") {
|
|
@@ -280,16 +256,14 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
|
|
|
280
256
|
}
|
|
281
257
|
if (key === "Program") {
|
|
282
258
|
wrappedVisitors.Program = (node) => {
|
|
283
|
-
|
|
284
|
-
fileImportsReactRuntime = runtimeImports.hasReactRuntime;
|
|
285
|
-
fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
|
|
259
|
+
fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
|
|
286
260
|
visitor(node);
|
|
287
261
|
};
|
|
288
262
|
continue;
|
|
289
263
|
}
|
|
290
264
|
if (key === "JSXOpeningElement") {
|
|
291
265
|
wrappedVisitors.JSXOpeningElement = (node) => {
|
|
292
|
-
if (!
|
|
266
|
+
if (!fileIsNonReactJsx && jsxAttributeIsNonReactDialectMarker(node)) fileIsNonReactJsx = true;
|
|
293
267
|
if (fileIsNonReactJsx) return;
|
|
294
268
|
visitor(node);
|
|
295
269
|
};
|
|
@@ -301,9 +275,7 @@ const wrapCreateForReactJsxOnly = (create) => ((context) => {
|
|
|
301
275
|
};
|
|
302
276
|
}
|
|
303
277
|
if (!("Program" in wrappedVisitors)) wrappedVisitors.Program = (node) => {
|
|
304
|
-
|
|
305
|
-
fileImportsReactRuntime = runtimeImports.hasReactRuntime;
|
|
306
|
-
fileIsNonReactJsx = runtimeImports.hasNonReactRuntime && !runtimeImports.hasReactRuntime;
|
|
278
|
+
fileIsNonReactJsx = fileImportsNonReactJsxDialect(node);
|
|
307
279
|
};
|
|
308
280
|
return wrappedVisitors;
|
|
309
281
|
});
|
|
@@ -4342,397 +4314,6 @@ const findTransparentExpressionRoot = (node) => {
|
|
|
4342
4314
|
return current;
|
|
4343
4315
|
};
|
|
4344
4316
|
//#endregion
|
|
4345
|
-
//#region src/plugin/utils/get-static-property-key-name.ts
|
|
4346
|
-
const getStaticPropertyKeyName = (node, options = {}) => {
|
|
4347
|
-
if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
|
|
4348
|
-
const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
|
|
4349
|
-
if (node.computed) {
|
|
4350
|
-
if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
|
|
4351
|
-
if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
|
|
4352
|
-
return null;
|
|
4353
|
-
}
|
|
4354
|
-
if (isNodeOfType(key, "Identifier")) return key.name;
|
|
4355
|
-
if (isNodeOfType(key, "Literal")) {
|
|
4356
|
-
if (typeof key.value === "string") return key.value;
|
|
4357
|
-
if (options.stringifyNonStringLiterals) return String(key.value);
|
|
4358
|
-
}
|
|
4359
|
-
return null;
|
|
4360
|
-
};
|
|
4361
|
-
//#endregion
|
|
4362
|
-
//#region src/plugin/utils/get-destructured-binding-property-name.ts
|
|
4363
|
-
const getDestructuredBindingPropertyName = (bindingIdentifier) => {
|
|
4364
|
-
let bindingNode = bindingIdentifier;
|
|
4365
|
-
if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
|
|
4366
|
-
const property = bindingNode.parent;
|
|
4367
|
-
if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
|
|
4368
|
-
return getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
4369
|
-
};
|
|
4370
|
-
//#endregion
|
|
4371
|
-
//#region src/plugin/utils/get-static-property-name.ts
|
|
4372
|
-
const getStaticPropertyName = (memberExpression) => {
|
|
4373
|
-
const property = memberExpression.property;
|
|
4374
|
-
if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
|
|
4375
|
-
if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
|
|
4376
|
-
if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
|
|
4377
|
-
return null;
|
|
4378
|
-
};
|
|
4379
|
-
//#endregion
|
|
4380
|
-
//#region src/plugin/utils/has-static-property-write-before.ts
|
|
4381
|
-
const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
|
|
4382
|
-
const potentiallyAliasedSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
|
|
4383
|
-
const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
|
|
4384
|
-
"CatchClause",
|
|
4385
|
-
"ConditionalExpression",
|
|
4386
|
-
"DoWhileStatement",
|
|
4387
|
-
"ForInStatement",
|
|
4388
|
-
"ForOfStatement",
|
|
4389
|
-
"ForStatement",
|
|
4390
|
-
"IfStatement",
|
|
4391
|
-
"LogicalExpression",
|
|
4392
|
-
"SwitchCase",
|
|
4393
|
-
"SwitchStatement",
|
|
4394
|
-
"TryStatement",
|
|
4395
|
-
"WhileStatement"
|
|
4396
|
-
]);
|
|
4397
|
-
const getResolvedStaticPropertyName = (memberExpression, scopes) => {
|
|
4398
|
-
if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
|
|
4399
|
-
const directPropertyName = getStaticPropertyName(memberExpression);
|
|
4400
|
-
if (directPropertyName || !memberExpression.computed) return directPropertyName;
|
|
4401
|
-
const property = stripParenExpression(memberExpression.property);
|
|
4402
|
-
if (!isNodeOfType(property, "Identifier")) return null;
|
|
4403
|
-
const propertySymbol = resolveConstIdentifierAlias(property, scopes);
|
|
4404
|
-
const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
|
|
4405
|
-
return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
|
|
4406
|
-
};
|
|
4407
|
-
const collectScopeSymbols = (scope, symbols) => {
|
|
4408
|
-
symbols.push(...scope.symbols);
|
|
4409
|
-
for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
|
|
4410
|
-
};
|
|
4411
|
-
const getEquivalentSymbols = (identifier, scopes) => {
|
|
4412
|
-
const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
4413
|
-
if (!rootSymbol) return [];
|
|
4414
|
-
let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
|
|
4415
|
-
if (!symbolsByRootId) {
|
|
4416
|
-
symbolsByRootId = /* @__PURE__ */ new Map();
|
|
4417
|
-
equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
|
|
4418
|
-
}
|
|
4419
|
-
const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
|
|
4420
|
-
if (cachedSymbols) return cachedSymbols;
|
|
4421
|
-
const allSymbols = [];
|
|
4422
|
-
collectScopeSymbols(scopes.rootScope, allSymbols);
|
|
4423
|
-
const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
|
|
4424
|
-
symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
|
|
4425
|
-
return equivalentSymbols;
|
|
4426
|
-
};
|
|
4427
|
-
const getDirectAliasSourceSymbol = (expression, scopes) => {
|
|
4428
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
4429
|
-
return isNodeOfType(unwrappedExpression, "Identifier") ? scopes.symbolFor(unwrappedExpression) : null;
|
|
4430
|
-
};
|
|
4431
|
-
const getDirectAssignmentSourceSymbol = (identifier, scopes) => {
|
|
4432
|
-
const assignmentTarget = findTransparentExpressionRoot(identifier);
|
|
4433
|
-
const parent = assignmentTarget.parent;
|
|
4434
|
-
if (!parent || !isNodeOfType(parent, "AssignmentExpression") || parent.operator !== "=" || parent.left !== assignmentTarget) return null;
|
|
4435
|
-
return getDirectAliasSourceSymbol(parent.right, scopes);
|
|
4436
|
-
};
|
|
4437
|
-
const isDirectAliasSourceReference = (identifier) => {
|
|
4438
|
-
const aliasSource = findTransparentExpressionRoot(identifier);
|
|
4439
|
-
const parent = aliasSource.parent;
|
|
4440
|
-
if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === aliasSource && (isNodeOfType(parent.id, "Identifier") || isNodeOfType(parent.id, "ObjectPattern"))) return true;
|
|
4441
|
-
return Boolean(parent && isNodeOfType(parent, "AssignmentExpression") && parent.operator === "=" && parent.right === aliasSource && isNodeOfType(stripParenExpression(parent.left), "Identifier"));
|
|
4442
|
-
};
|
|
4443
|
-
const isDirectAliasOfKnownSymbol = (symbol, knownSymbolIds, scopes) => {
|
|
4444
|
-
if (symbol.initializer && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && symbol.declarationNode.id === symbol.bindingIdentifier) {
|
|
4445
|
-
const initializerSymbol = getDirectAliasSourceSymbol(symbol.initializer, scopes);
|
|
4446
|
-
if (initializerSymbol && knownSymbolIds.has(initializerSymbol.id)) return true;
|
|
4447
|
-
}
|
|
4448
|
-
return symbol.references.some((reference) => {
|
|
4449
|
-
const assignmentSourceSymbol = getDirectAssignmentSourceSymbol(reference.identifier, scopes);
|
|
4450
|
-
return Boolean(assignmentSourceSymbol && knownSymbolIds.has(assignmentSourceSymbol.id));
|
|
4451
|
-
});
|
|
4452
|
-
};
|
|
4453
|
-
const getPotentiallyAliasedSymbols = (identifier, scopes) => {
|
|
4454
|
-
const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
4455
|
-
if (!rootSymbol) return [];
|
|
4456
|
-
let symbolsByRootId = potentiallyAliasedSymbolsByAnalysis.get(scopes);
|
|
4457
|
-
if (!symbolsByRootId) {
|
|
4458
|
-
symbolsByRootId = /* @__PURE__ */ new Map();
|
|
4459
|
-
potentiallyAliasedSymbolsByAnalysis.set(scopes, symbolsByRootId);
|
|
4460
|
-
}
|
|
4461
|
-
const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
|
|
4462
|
-
if (cachedSymbols) return cachedSymbols;
|
|
4463
|
-
const allSymbols = [];
|
|
4464
|
-
collectScopeSymbols(scopes.rootScope, allSymbols);
|
|
4465
|
-
const aliasedSymbolIds = new Set([rootSymbol.id]);
|
|
4466
|
-
let didAddAlias = true;
|
|
4467
|
-
while (didAddAlias) {
|
|
4468
|
-
didAddAlias = false;
|
|
4469
|
-
for (const symbol of allSymbols) {
|
|
4470
|
-
if (aliasedSymbolIds.has(symbol.id)) continue;
|
|
4471
|
-
if (!isDirectAliasOfKnownSymbol(symbol, aliasedSymbolIds, scopes)) continue;
|
|
4472
|
-
aliasedSymbolIds.add(symbol.id);
|
|
4473
|
-
didAddAlias = true;
|
|
4474
|
-
}
|
|
4475
|
-
}
|
|
4476
|
-
const aliasedSymbols = allSymbols.filter((symbol) => aliasedSymbolIds.has(symbol.id));
|
|
4477
|
-
symbolsByRootId.set(rootSymbol.id, aliasedSymbols);
|
|
4478
|
-
return aliasedSymbols;
|
|
4479
|
-
};
|
|
4480
|
-
const findExecutionBoundary = (node) => {
|
|
4481
|
-
let current = node;
|
|
4482
|
-
while (current) {
|
|
4483
|
-
if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
|
|
4484
|
-
current = current.parent ?? null;
|
|
4485
|
-
}
|
|
4486
|
-
return null;
|
|
4487
|
-
};
|
|
4488
|
-
const isOnUnconditionalPath = (node, boundary) => {
|
|
4489
|
-
let current = node.parent ?? null;
|
|
4490
|
-
while (current && current !== boundary) {
|
|
4491
|
-
if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
|
|
4492
|
-
current = current.parent ?? null;
|
|
4493
|
-
}
|
|
4494
|
-
return current === boundary;
|
|
4495
|
-
};
|
|
4496
|
-
const findFunctionBindingIdentifier = (functionNode) => {
|
|
4497
|
-
if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
|
|
4498
|
-
const parent = functionNode.parent;
|
|
4499
|
-
if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
|
|
4500
|
-
return null;
|
|
4501
|
-
};
|
|
4502
|
-
const findDirectCall = (identifier) => {
|
|
4503
|
-
let callee = identifier;
|
|
4504
|
-
let parent = callee.parent;
|
|
4505
|
-
while (parent && stripParenExpression(parent) === identifier) {
|
|
4506
|
-
callee = parent;
|
|
4507
|
-
parent = callee.parent;
|
|
4508
|
-
}
|
|
4509
|
-
return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
|
|
4510
|
-
};
|
|
4511
|
-
const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
|
|
4512
|
-
if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
|
|
4513
|
-
visitedFunctionNodes.add(functionNode);
|
|
4514
|
-
const referenceBoundary = findExecutionBoundary(referenceNode);
|
|
4515
|
-
if (!referenceBoundary) return false;
|
|
4516
|
-
const invocationCalls = [];
|
|
4517
|
-
const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
|
|
4518
|
-
if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
|
|
4519
|
-
const call = findDirectCall(reference.identifier);
|
|
4520
|
-
if (call) invocationCalls.push(call);
|
|
4521
|
-
}
|
|
4522
|
-
else {
|
|
4523
|
-
const call = findDirectCall(functionNode);
|
|
4524
|
-
if (call) invocationCalls.push(call);
|
|
4525
|
-
}
|
|
4526
|
-
return invocationCalls.some((call) => {
|
|
4527
|
-
if (call.range[0] >= referenceNode.range[0]) return false;
|
|
4528
|
-
const callBoundary = findExecutionBoundary(call);
|
|
4529
|
-
if (!callBoundary) return false;
|
|
4530
|
-
if (callBoundary === referenceBoundary) return true;
|
|
4531
|
-
if (!isFunctionLike$2(callBoundary)) return false;
|
|
4532
|
-
return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
|
|
4533
|
-
});
|
|
4534
|
-
};
|
|
4535
|
-
const isMemberWriteTarget = (memberExpression) => {
|
|
4536
|
-
const parent = memberExpression.parent;
|
|
4537
|
-
if (!parent) return false;
|
|
4538
|
-
if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
|
|
4539
|
-
if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
|
|
4540
|
-
return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
|
|
4541
|
-
};
|
|
4542
|
-
const getMemberWriteTarget = (identifier) => {
|
|
4543
|
-
let parent = identifier.parent;
|
|
4544
|
-
while (parent && stripParenExpression(parent) === identifier) parent = parent.parent;
|
|
4545
|
-
if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== identifier || !isMemberWriteTarget(parent)) return null;
|
|
4546
|
-
return parent;
|
|
4547
|
-
};
|
|
4548
|
-
const getStaticPropertyWriteTarget = (identifier, propertyName, scopes) => {
|
|
4549
|
-
const writeTarget = getMemberWriteTarget(identifier);
|
|
4550
|
-
return writeTarget && getResolvedStaticPropertyName(writeTarget, scopes) === propertyName ? writeTarget : null;
|
|
4551
|
-
};
|
|
4552
|
-
const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
|
|
4553
|
-
const writeTarget = getStaticPropertyWriteTarget(reference.identifier, propertyName, scopes);
|
|
4554
|
-
if (!writeTarget) return false;
|
|
4555
|
-
const writeBoundary = findExecutionBoundary(writeTarget);
|
|
4556
|
-
const referenceBoundary = findExecutionBoundary(referenceNode);
|
|
4557
|
-
if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(writeTarget, writeBoundary)) return false;
|
|
4558
|
-
if (writeBoundary === referenceBoundary) return writeTarget.range[0] < referenceNode.range[0];
|
|
4559
|
-
return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
|
|
4560
|
-
});
|
|
4561
|
-
const isStableStaticPropertyReference = (identifier) => {
|
|
4562
|
-
if (isDirectAliasSourceReference(identifier)) return true;
|
|
4563
|
-
const identifierRoot = findTransparentExpressionRoot(identifier);
|
|
4564
|
-
const memberExpression = identifierRoot.parent;
|
|
4565
|
-
return Boolean(memberExpression && isNodeOfType(memberExpression, "MemberExpression") && stripParenExpression(memberExpression.object) === identifierRoot);
|
|
4566
|
-
};
|
|
4567
|
-
const hasPossibleStaticPropertyWrite = (identifier, propertyName, scopes) => {
|
|
4568
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
4569
|
-
return getPotentiallyAliasedSymbols(identifier, scopes).some((symbol) => symbol.references.some((reference) => {
|
|
4570
|
-
const writeTarget = getMemberWriteTarget(reference.identifier);
|
|
4571
|
-
if (!writeTarget) return false;
|
|
4572
|
-
const writtenPropertyName = getResolvedStaticPropertyName(writeTarget, scopes);
|
|
4573
|
-
return writtenPropertyName === null || writtenPropertyName === propertyName;
|
|
4574
|
-
}));
|
|
4575
|
-
};
|
|
4576
|
-
const hasPossibleStaticPropertyMutationOrEscape = (identifier, propertyName, scopes) => {
|
|
4577
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
4578
|
-
if (hasPossibleStaticPropertyWrite(identifier, propertyName, scopes)) return true;
|
|
4579
|
-
return getPotentiallyAliasedSymbols(identifier, scopes).some((symbol) => symbol.references.some((reference) => !isStableStaticPropertyReference(reference.identifier)));
|
|
4580
|
-
};
|
|
4581
|
-
const hasPossibleStaticMemberCallWrite = (callExpression, scopes) => {
|
|
4582
|
-
const unwrappedCallExpression = stripParenExpression(callExpression);
|
|
4583
|
-
if (!isNodeOfType(unwrappedCallExpression, "CallExpression")) return false;
|
|
4584
|
-
const callee = stripParenExpression(unwrappedCallExpression.callee);
|
|
4585
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
4586
|
-
const propertyName = getStaticPropertyName(callee);
|
|
4587
|
-
if (propertyName === null) return false;
|
|
4588
|
-
const receiver = stripParenExpression(callee.object);
|
|
4589
|
-
return isNodeOfType(receiver, "Identifier") && hasPossibleStaticPropertyWrite(receiver, propertyName, scopes);
|
|
4590
|
-
};
|
|
4591
|
-
const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
|
|
4592
|
-
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
4593
|
-
return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
|
|
4594
|
-
};
|
|
4595
|
-
//#endregion
|
|
4596
|
-
//#region src/plugin/utils/find-enclosing-function.ts
|
|
4597
|
-
const findEnclosingFunction$1 = (node) => {
|
|
4598
|
-
let cursor = node.parent;
|
|
4599
|
-
while (cursor) {
|
|
4600
|
-
if (isFunctionLike$2(cursor)) return cursor;
|
|
4601
|
-
cursor = cursor.parent ?? null;
|
|
4602
|
-
}
|
|
4603
|
-
return null;
|
|
4604
|
-
};
|
|
4605
|
-
//#endregion
|
|
4606
|
-
//#region src/plugin/utils/has-symbol-write-before.ts
|
|
4607
|
-
const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
|
|
4608
|
-
if (reference.flag === "read") return false;
|
|
4609
|
-
const writeFunction = findEnclosingFunction$1(reference.identifier);
|
|
4610
|
-
if (writeFunction === findEnclosingFunction$1(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
|
|
4611
|
-
if (!writeFunction) return true;
|
|
4612
|
-
return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
|
|
4613
|
-
});
|
|
4614
|
-
//#endregion
|
|
4615
|
-
//#region src/plugin/utils/get-order-independent-local-function.ts
|
|
4616
|
-
const COMMUTATIVE_COMPOUND_ASSIGNMENT_OPERATORS = new Set([
|
|
4617
|
-
"+=",
|
|
4618
|
-
"-=",
|
|
4619
|
-
"*=",
|
|
4620
|
-
"/=",
|
|
4621
|
-
"%=",
|
|
4622
|
-
"&=",
|
|
4623
|
-
"^=",
|
|
4624
|
-
"|="
|
|
4625
|
-
]);
|
|
4626
|
-
const isPureParameterExpression = (expression, parameterNames) => {
|
|
4627
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
4628
|
-
if (isNodeOfType(unwrappedExpression, "Literal")) return true;
|
|
4629
|
-
if (isNodeOfType(unwrappedExpression, "Identifier")) return parameterNames.has(unwrappedExpression.name);
|
|
4630
|
-
if (isNodeOfType(unwrappedExpression, "BinaryExpression") || isNodeOfType(unwrappedExpression, "LogicalExpression")) return isPureParameterExpression(unwrappedExpression.left, parameterNames) && isPureParameterExpression(unwrappedExpression.right, parameterNames);
|
|
4631
|
-
if (isNodeOfType(unwrappedExpression, "UnaryExpression")) return unwrappedExpression.operator !== "delete" && isPureParameterExpression(unwrappedExpression.argument, parameterNames);
|
|
4632
|
-
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isPureParameterExpression(unwrappedExpression.test, parameterNames) && isPureParameterExpression(unwrappedExpression.consequent, parameterNames) && isPureParameterExpression(unwrappedExpression.alternate, parameterNames);
|
|
4633
|
-
if (isNodeOfType(unwrappedExpression, "TemplateLiteral")) return unwrappedExpression.expressions.every((nestedExpression) => isPureParameterExpression(nestedExpression, parameterNames));
|
|
4634
|
-
return false;
|
|
4635
|
-
};
|
|
4636
|
-
const isOrderIndependentPromiseResolveCall = (expression, parameterNames, scopes) => {
|
|
4637
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
4638
|
-
if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
|
|
4639
|
-
if (!unwrappedExpression.arguments.every((argument) => !isNodeOfType(argument, "SpreadElement") && isPureParameterExpression(argument, parameterNames))) return false;
|
|
4640
|
-
const callee = stripParenExpression(unwrappedExpression.callee);
|
|
4641
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
4642
|
-
if (getStaticPropertyName(callee) !== "resolve") return false;
|
|
4643
|
-
const receiver = stripParenExpression(callee.object);
|
|
4644
|
-
return isNodeOfType(receiver, "Identifier") && receiver.name === "Promise" && scopes.isGlobalReference(receiver);
|
|
4645
|
-
};
|
|
4646
|
-
const isHarmlessPromiseResolveAwait = (statement, parameterNames, scopes) => {
|
|
4647
|
-
if (!isNodeOfType(statement, "ExpressionStatement") || !isNodeOfType(statement.expression, "AwaitExpression")) return false;
|
|
4648
|
-
return isOrderIndependentPromiseResolveCall(statement.expression.argument, parameterNames, scopes);
|
|
4649
|
-
};
|
|
4650
|
-
const isCommutativeParameterMutation = (statement, parameterNames) => {
|
|
4651
|
-
if (!isNodeOfType(statement, "ExpressionStatement") || !isNodeOfType(statement.expression, "AssignmentExpression") || !COMMUTATIVE_COMPOUND_ASSIGNMENT_OPERATORS.has(statement.expression.operator)) return false;
|
|
4652
|
-
const mutationTarget = stripParenExpression(statement.expression.left);
|
|
4653
|
-
if (!isNodeOfType(mutationTarget, "MemberExpression")) return false;
|
|
4654
|
-
if (getStaticPropertyName(mutationTarget) === null) return false;
|
|
4655
|
-
const receiver = stripParenExpression(mutationTarget.object);
|
|
4656
|
-
return isNodeOfType(receiver, "Identifier") && parameterNames.has(receiver.name) && isNodeOfType(stripParenExpression(statement.expression.right), "Literal");
|
|
4657
|
-
};
|
|
4658
|
-
const isOrderIndependentFunction = (functionNode, scopes) => {
|
|
4659
|
-
if (!isFunctionLike$2(functionNode)) return false;
|
|
4660
|
-
const parameterNames = /* @__PURE__ */ new Set();
|
|
4661
|
-
for (const parameter of functionNode.params) {
|
|
4662
|
-
if (!isNodeOfType(parameter, "Identifier")) return false;
|
|
4663
|
-
parameterNames.add(parameter.name);
|
|
4664
|
-
}
|
|
4665
|
-
if (!functionNode.async) {
|
|
4666
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isOrderIndependentPromiseResolveCall(functionNode.body, parameterNames, scopes);
|
|
4667
|
-
if (functionNode.body.body.length !== 1) return false;
|
|
4668
|
-
const [returnStatement] = functionNode.body.body;
|
|
4669
|
-
return Boolean(isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument && isOrderIndependentPromiseResolveCall(returnStatement.argument, parameterNames, scopes));
|
|
4670
|
-
}
|
|
4671
|
-
if (!isNodeOfType(functionNode.body, "BlockStatement")) return isPureParameterExpression(functionNode.body, parameterNames);
|
|
4672
|
-
const statements = functionNode.body.body;
|
|
4673
|
-
for (let statementIndex = 0; statementIndex < statements.length; statementIndex++) {
|
|
4674
|
-
const statement = statements[statementIndex];
|
|
4675
|
-
const isTerminalStatement = statementIndex === statements.length - 1;
|
|
4676
|
-
if (isHarmlessPromiseResolveAwait(statement, parameterNames, scopes)) continue;
|
|
4677
|
-
if (isNodeOfType(statement, "ExpressionStatement") && isPureParameterExpression(statement.expression, parameterNames)) continue;
|
|
4678
|
-
if (isCommutativeParameterMutation(statement, parameterNames)) return isTerminalStatement;
|
|
4679
|
-
if (!isNodeOfType(statement, "ReturnStatement") || !isTerminalStatement) return false;
|
|
4680
|
-
return !statement.argument || isPureParameterExpression(statement.argument, parameterNames) || isOrderIndependentPromiseResolveCall(statement.argument, parameterNames, scopes);
|
|
4681
|
-
}
|
|
4682
|
-
return true;
|
|
4683
|
-
};
|
|
4684
|
-
const getObjectPropertyName = (property) => {
|
|
4685
|
-
if (!isNodeOfType(property, "Property")) return null;
|
|
4686
|
-
return getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
4687
|
-
};
|
|
4688
|
-
const resolveOrderIndependentObjectPropertyFunction = (objectExpression, propertyName, callExpression, scopes, visitedSymbolIds) => {
|
|
4689
|
-
const unwrappedObject = stripParenExpression(objectExpression);
|
|
4690
|
-
if (isNodeOfType(unwrappedObject, "Identifier")) {
|
|
4691
|
-
const symbol = scopes.symbolFor(unwrappedObject);
|
|
4692
|
-
if (!symbol || symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, callExpression, scopes) || hasPossibleStaticPropertyMutationOrEscape(unwrappedObject, propertyName, scopes)) return null;
|
|
4693
|
-
visitedSymbolIds.add(symbol.id);
|
|
4694
|
-
return resolveOrderIndependentObjectPropertyFunction(symbol.initializer, propertyName, callExpression, scopes, visitedSymbolIds);
|
|
4695
|
-
}
|
|
4696
|
-
if (!isNodeOfType(unwrappedObject, "ObjectExpression")) return null;
|
|
4697
|
-
let matchingProperty = null;
|
|
4698
|
-
for (const property of unwrappedObject.properties) {
|
|
4699
|
-
if (!isNodeOfType(property, "Property")) return null;
|
|
4700
|
-
if (property.kind !== "init") return null;
|
|
4701
|
-
const candidatePropertyName = getObjectPropertyName(property);
|
|
4702
|
-
if (candidatePropertyName === null) return null;
|
|
4703
|
-
if (candidatePropertyName === propertyName) matchingProperty = property;
|
|
4704
|
-
}
|
|
4705
|
-
if (!matchingProperty || !isNodeOfType(matchingProperty, "Property")) return null;
|
|
4706
|
-
const propertyValue = stripParenExpression(matchingProperty.value);
|
|
4707
|
-
if (isFunctionLike$2(propertyValue)) return isOrderIndependentFunction(propertyValue, scopes) ? propertyValue : null;
|
|
4708
|
-
return resolveOrderIndependentLocalFunction(propertyValue, callExpression, scopes, visitedSymbolIds);
|
|
4709
|
-
};
|
|
4710
|
-
const resolveOrderIndependentLocalFunction = (callee, callExpression, scopes, visitedSymbolIds) => {
|
|
4711
|
-
const unwrappedCallee = stripParenExpression(callee);
|
|
4712
|
-
if (isNodeOfType(unwrappedCallee, "MemberExpression")) {
|
|
4713
|
-
const propertyName = getStaticPropertyName(unwrappedCallee);
|
|
4714
|
-
if (propertyName === null) return null;
|
|
4715
|
-
return resolveOrderIndependentObjectPropertyFunction(stripParenExpression(unwrappedCallee.object), propertyName, callExpression, scopes, visitedSymbolIds);
|
|
4716
|
-
}
|
|
4717
|
-
if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
|
|
4718
|
-
const symbol = scopes.symbolFor(unwrappedCallee);
|
|
4719
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, callExpression, scopes)) return null;
|
|
4720
|
-
visitedSymbolIds.add(symbol.id);
|
|
4721
|
-
if (!symbol.initializer) return null;
|
|
4722
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
4723
|
-
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
4724
|
-
if (destructuredPropertyName !== null) return resolveOrderIndependentObjectPropertyFunction(initializer, destructuredPropertyName, callExpression, scopes, visitedSymbolIds);
|
|
4725
|
-
if (isFunctionLike$2(initializer)) return isOrderIndependentFunction(initializer, scopes) ? initializer : null;
|
|
4726
|
-
if (symbol.kind !== "const") return null;
|
|
4727
|
-
return resolveOrderIndependentLocalFunction(initializer, callExpression, scopes, visitedSymbolIds);
|
|
4728
|
-
};
|
|
4729
|
-
const getOrderIndependentLocalFunction = (callExpression, scopes) => {
|
|
4730
|
-
const unwrappedCall = stripParenExpression(callExpression);
|
|
4731
|
-
if (!isNodeOfType(unwrappedCall, "CallExpression")) return null;
|
|
4732
|
-
if (hasPossibleStaticMemberCallWrite(unwrappedCall, scopes)) return null;
|
|
4733
|
-
return resolveOrderIndependentLocalFunction(unwrappedCall.callee, unwrappedCall, scopes, /* @__PURE__ */ new Set());
|
|
4734
|
-
};
|
|
4735
|
-
//#endregion
|
|
4736
4317
|
//#region src/plugin/utils/is-inline-function-expression.ts
|
|
4737
4318
|
/**
|
|
4738
4319
|
* Type-guard for the two "inline function expression" ESTree forms:
|
|
@@ -4766,15 +4347,13 @@ const isIntentionalSequencingCallee = (callee) => {
|
|
|
4766
4347
|
if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier")) return INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(callee.property.name);
|
|
4767
4348
|
return false;
|
|
4768
4349
|
};
|
|
4769
|
-
const isAwaitingSleepLikeCall = (awaitNode
|
|
4350
|
+
const isAwaitingSleepLikeCall = (awaitNode) => {
|
|
4770
4351
|
if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
|
|
4771
4352
|
const argument = awaitNode.argument;
|
|
4772
4353
|
if (!argument) return false;
|
|
4773
4354
|
if (!isNodeOfType(argument, "CallExpression")) return false;
|
|
4774
|
-
if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) return false;
|
|
4775
4355
|
return isIntentionalSequencingCallee(argument.callee);
|
|
4776
4356
|
};
|
|
4777
|
-
const isAwaitingPossiblyMutatedMemberCall = (awaitNode, context) => isNodeOfType(awaitNode, "AwaitExpression") && Boolean(awaitNode.argument) && hasPossibleStaticMemberCallWrite(awaitNode.argument, context.scopes);
|
|
4778
4357
|
const PROMISE_CONCURRENCY_METHODS = new Set([
|
|
4779
4358
|
"all",
|
|
4780
4359
|
"allSettled",
|
|
@@ -4817,7 +4396,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
|
|
|
4817
4396
|
});
|
|
4818
4397
|
return isWaitLike;
|
|
4819
4398
|
};
|
|
4820
|
-
const isIntentionallySequentialAwait = (awaitNode
|
|
4399
|
+
const isIntentionallySequentialAwait = (awaitNode) => isAwaitingSleepLikeCall(awaitNode) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
|
|
4821
4400
|
const collectPatternIdentifiers = (pattern, target) => {
|
|
4822
4401
|
if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
|
|
4823
4402
|
else if (isNodeOfType(pattern, "ObjectPattern")) {
|
|
@@ -5008,12 +4587,12 @@ const getLoopLabelName = (loopNode) => {
|
|
|
5008
4587
|
if (isNodeOfType(parent, "LabeledStatement") && isNodeOfType(parent.label, "Identifier")) return parent.label.name;
|
|
5009
4588
|
return null;
|
|
5010
4589
|
};
|
|
5011
|
-
const loopBodyHasIntentionallySequentialAwait = (block
|
|
4590
|
+
const loopBodyHasIntentionallySequentialAwait = (block) => {
|
|
5012
4591
|
let foundIntentional = false;
|
|
5013
4592
|
walkAst(block, (child) => {
|
|
5014
4593
|
if (foundIntentional) return false;
|
|
5015
4594
|
if (isInlineFunctionExpression(child) || isNodeOfType(child, "FunctionDeclaration")) return false;
|
|
5016
|
-
if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child
|
|
4595
|
+
if (isNodeOfType(child, "AwaitExpression") && isIntentionallySequentialAwait(child)) {
|
|
5017
4596
|
foundIntentional = true;
|
|
5018
4597
|
return false;
|
|
5019
4598
|
}
|
|
@@ -5118,7 +4697,7 @@ const asyncAwaitInLoop = defineRule({
|
|
|
5118
4697
|
const inspectLoop = (loopNode, label) => {
|
|
5119
4698
|
const loopBody = loopNode.body;
|
|
5120
4699
|
if (!loopBody) return;
|
|
5121
|
-
if (loopBodyHasIntentionallySequentialAwait(loopBody
|
|
4700
|
+
if (loopBodyHasIntentionallySequentialAwait(loopBody)) return;
|
|
5122
4701
|
if ((isNodeOfType(loopNode, "WhileStatement") || isNodeOfType(loopNode, "DoWhileStatement")) && isLoopTestDependentOnBodyState(loopNode.test, loopBody)) return;
|
|
5123
4702
|
if (hasLoopCarriedDependency(loopBody)) return;
|
|
5124
4703
|
if (loopBodyHasAwaitDependentEarlyExit(loopBody, getLoopLabelName(loopNode))) return;
|
|
@@ -5378,7 +4957,7 @@ const guardConsequentPerformsSideEffects = (consequent) => {
|
|
|
5378
4957
|
});
|
|
5379
4958
|
return performsSideEffects;
|
|
5380
4959
|
};
|
|
5381
|
-
const findEnclosingFunction = (node) => {
|
|
4960
|
+
const findEnclosingFunction$1 = (node) => {
|
|
5382
4961
|
let ancestor = node.parent;
|
|
5383
4962
|
while (ancestor) {
|
|
5384
4963
|
if (isFunctionLike$2(ancestor)) return ancestor;
|
|
@@ -5391,7 +4970,7 @@ const guardTestReadsReassignedLocal = (test, guardStatement) => {
|
|
|
5391
4970
|
const testIdentifierNames = /* @__PURE__ */ new Set();
|
|
5392
4971
|
collectReferenceIdentifierNames(test, testIdentifierNames);
|
|
5393
4972
|
if (testIdentifierNames.size === 0) return false;
|
|
5394
|
-
const enclosingFunction = findEnclosingFunction(guardStatement);
|
|
4973
|
+
const enclosingFunction = findEnclosingFunction$1(guardStatement);
|
|
5395
4974
|
if (!enclosingFunction || !isFunctionLike$2(enclosingFunction) || !enclosingFunction.body) return false;
|
|
5396
4975
|
let readsReassignedLocal = false;
|
|
5397
4976
|
walkAst(enclosingFunction.body, (child) => {
|
|
@@ -5562,36 +5141,19 @@ const isIntentionalSequencingAwait = (awaitedCall) => {
|
|
|
5562
5141
|
return getCalleeIdentifierTrail(awaitedCall).some((name) => INTENTIONAL_SEQUENCING_CALLEE_NAMES.has(name));
|
|
5563
5142
|
};
|
|
5564
5143
|
const isBareExpressionAwait = (statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "AwaitExpression");
|
|
5565
|
-
const hasOrderIndependentBareAwaitArguments = (callExpression) => {
|
|
5566
|
-
const unwrappedCallExpression = stripParenExpression(callExpression);
|
|
5567
|
-
if (!isNodeOfType(unwrappedCallExpression, "CallExpression")) return false;
|
|
5568
|
-
return unwrappedCallExpression.arguments.every((argument) => {
|
|
5569
|
-
if (isNodeOfType(argument, "SpreadElement")) return false;
|
|
5570
|
-
const unwrappedArgument = stripParenExpression(argument);
|
|
5571
|
-
return isNodeOfType(unwrappedArgument, "Identifier") || isNodeOfType(unwrappedArgument, "Literal");
|
|
5572
|
-
});
|
|
5573
|
-
};
|
|
5574
5144
|
const isNonCallAwait = (statement) => {
|
|
5575
5145
|
const awaitedExpression = getAwaitedCall(statement);
|
|
5576
5146
|
if (!awaitedExpression) return false;
|
|
5577
5147
|
const stripped = stripParenExpression(awaitedExpression);
|
|
5578
5148
|
return !isNodeOfType(stripped, "CallExpression") && !isNodeOfType(stripped, "NewExpression") && !isNodeOfType(stripped, "ImportExpression");
|
|
5579
5149
|
};
|
|
5580
|
-
const sequenceContainsSerializationSignal = (statements
|
|
5581
|
-
let bareAwaitFunction = null;
|
|
5150
|
+
const sequenceContainsSerializationSignal = (statements) => {
|
|
5582
5151
|
for (const statement of statements) {
|
|
5152
|
+
if (isBareExpressionAwait(statement)) return true;
|
|
5583
5153
|
if (isNonCallAwait(statement)) return true;
|
|
5584
5154
|
const awaitedCall = getAwaitedCall(statement);
|
|
5585
|
-
if (awaitedCall && hasPossibleStaticMemberCallWrite(awaitedCall, context.scopes)) return true;
|
|
5586
|
-
const orderIndependentFunction = awaitedCall ? getOrderIndependentLocalFunction(awaitedCall, context.scopes) : null;
|
|
5587
|
-
if (isBareExpressionAwait(statement)) {
|
|
5588
|
-
if (orderIndependentFunction === null) return true;
|
|
5589
|
-
if (!awaitedCall || !hasOrderIndependentBareAwaitArguments(awaitedCall)) return true;
|
|
5590
|
-
if (bareAwaitFunction !== null && bareAwaitFunction !== orderIndependentFunction) return true;
|
|
5591
|
-
bareAwaitFunction = orderIndependentFunction;
|
|
5592
|
-
}
|
|
5593
5155
|
if (isOrderedUiFlowAwait(awaitedCall)) return true;
|
|
5594
|
-
if (isIntentionalSequencingAwait(awaitedCall)
|
|
5156
|
+
if (isIntentionalSequencingAwait(awaitedCall)) return true;
|
|
5595
5157
|
}
|
|
5596
5158
|
return false;
|
|
5597
5159
|
};
|
|
@@ -5649,7 +5211,7 @@ const asyncParallel = defineRule({
|
|
|
5649
5211
|
const consecutiveAwaitStatements = [];
|
|
5650
5212
|
const flushConsecutiveAwaits = () => {
|
|
5651
5213
|
if (consecutiveAwaitStatements.length >= 3) {
|
|
5652
|
-
if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements
|
|
5214
|
+
if (!sequenceContainsSerializationSignal(consecutiveAwaitStatements)) reportIfIndependent(consecutiveAwaitStatements, context);
|
|
5653
5215
|
}
|
|
5654
5216
|
consecutiveAwaitStatements.length = 0;
|
|
5655
5217
|
};
|
|
@@ -6427,6 +5989,23 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
|
|
|
6427
5989
|
}
|
|
6428
5990
|
});
|
|
6429
5991
|
//#endregion
|
|
5992
|
+
//#region src/plugin/utils/get-static-property-key-name.ts
|
|
5993
|
+
const getStaticPropertyKeyName = (node, options = {}) => {
|
|
5994
|
+
if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
|
|
5995
|
+
const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
|
|
5996
|
+
if (node.computed) {
|
|
5997
|
+
if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
|
|
5998
|
+
if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
|
|
5999
|
+
return null;
|
|
6000
|
+
}
|
|
6001
|
+
if (isNodeOfType(key, "Identifier")) return key.name;
|
|
6002
|
+
if (isNodeOfType(key, "Literal")) {
|
|
6003
|
+
if (typeof key.value === "string") return key.value;
|
|
6004
|
+
if (options.stringifyNonStringLiterals) return String(key.value);
|
|
6005
|
+
}
|
|
6006
|
+
return null;
|
|
6007
|
+
};
|
|
6008
|
+
//#endregion
|
|
6430
6009
|
//#region src/plugin/utils/are-expressions-structurally-equal.ts
|
|
6431
6010
|
const areExpressionsStructurallyEqual = (a, b) => {
|
|
6432
6011
|
if (!a || !b) return a === b;
|
|
@@ -7110,6 +6689,15 @@ const getDirectConstInitializer = (symbol) => {
|
|
|
7110
6689
|
return symbol.initializer;
|
|
7111
6690
|
};
|
|
7112
6691
|
//#endregion
|
|
6692
|
+
//#region src/plugin/utils/get-static-property-name.ts
|
|
6693
|
+
const getStaticPropertyName = (memberExpression) => {
|
|
6694
|
+
const property = memberExpression.property;
|
|
6695
|
+
if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
|
|
6696
|
+
if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
|
|
6697
|
+
if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
|
|
6698
|
+
return null;
|
|
6699
|
+
};
|
|
6700
|
+
//#endregion
|
|
7113
6701
|
//#region src/plugin/utils/get-range-start.ts
|
|
7114
6702
|
const getRangeStart = (node) => {
|
|
7115
6703
|
const rangeStart = node.range?.[0];
|
|
@@ -7505,6 +7093,16 @@ const collectFunctionReturnStatements = (functionNode) => {
|
|
|
7505
7093
|
return returnStatements;
|
|
7506
7094
|
};
|
|
7507
7095
|
//#endregion
|
|
7096
|
+
//#region src/plugin/utils/find-enclosing-function.ts
|
|
7097
|
+
const findEnclosingFunction = (node) => {
|
|
7098
|
+
let cursor = node.parent;
|
|
7099
|
+
while (cursor) {
|
|
7100
|
+
if (isFunctionLike$2(cursor)) return cursor;
|
|
7101
|
+
cursor = cursor.parent ?? null;
|
|
7102
|
+
}
|
|
7103
|
+
return null;
|
|
7104
|
+
};
|
|
7105
|
+
//#endregion
|
|
7508
7106
|
//#region src/plugin/utils/statement-always-exits.ts
|
|
7509
7107
|
const statementAlwaysExits = (statement) => {
|
|
7510
7108
|
if (isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement")) return true;
|
|
@@ -7550,8 +7148,8 @@ const applyDefinitions = (incomingDefinitions, definitions) => {
|
|
|
7550
7148
|
const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow) => {
|
|
7551
7149
|
if (!REASSIGNABLE_BINDING_KINDS.has(symbol.kind)) return symbol.initializer ? [symbol.initializer] : [];
|
|
7552
7150
|
if (!controlFlow) return [];
|
|
7553
|
-
const referenceFunction = findEnclosingFunction
|
|
7554
|
-
if (findEnclosingFunction
|
|
7151
|
+
const referenceFunction = findEnclosingFunction(referenceNode);
|
|
7152
|
+
if (findEnclosingFunction(symbol.bindingIdentifier) !== referenceFunction) return [];
|
|
7555
7153
|
if (!referenceFunction) return [];
|
|
7556
7154
|
const functionControlFlow = controlFlow.cfgFor(referenceFunction);
|
|
7557
7155
|
if (!functionControlFlow) return [];
|
|
@@ -7575,7 +7173,7 @@ const collectPossibleAssignedExpressions = (symbol, referenceNode, controlFlow)
|
|
|
7575
7173
|
if (symbol.initializer) addDefinition(symbol.initializer, symbol.bindingIdentifier, bindingPosition);
|
|
7576
7174
|
for (const reference of symbol.references) {
|
|
7577
7175
|
const writePosition = getRangeStart(reference.identifier);
|
|
7578
|
-
if (reference.flag === "read" || findEnclosingFunction
|
|
7176
|
+
if (reference.flag === "read" || findEnclosingFunction(reference.identifier) !== referenceFunction || writePosition === null || writePosition >= referencePosition) continue;
|
|
7579
7177
|
const assignedExpression = getAssignedExpressionForWrite(reference.identifier);
|
|
7580
7178
|
if (!assignedExpression) continue;
|
|
7581
7179
|
addDefinition(assignedExpression, reference.identifier, writePosition);
|
|
@@ -9247,7 +8845,6 @@ const HTML_FOR_ATTRIBUTE = "htmlFor";
|
|
|
9247
8845
|
const LABEL_ELEMENT = "label";
|
|
9248
8846
|
const LABEL_COMPONENT_NAME = "Label";
|
|
9249
8847
|
const POLYMORPHIC_COMPONENT_PROP = "component";
|
|
9250
|
-
const TITLE_ATTRIBUTE = "title";
|
|
9251
8848
|
const WRAPPER_LABEL_PROP = "label";
|
|
9252
8849
|
const SELECT_ELEMENT = "select";
|
|
9253
8850
|
const DEFAULT_DEPTH = 5;
|
|
@@ -9293,96 +8890,6 @@ const hasNonEmptyPropValue = (attribute) => {
|
|
|
9293
8890
|
}
|
|
9294
8891
|
return true;
|
|
9295
8892
|
};
|
|
9296
|
-
const getLastJsxPropIgnoreCase = (attributes, targetProp) => {
|
|
9297
|
-
const targetPropLower = targetProp.toLowerCase();
|
|
9298
|
-
for (let attributeIndex = attributes.length - 1; attributeIndex >= 0; attributeIndex -= 1) {
|
|
9299
|
-
const attribute = attributes[attributeIndex];
|
|
9300
|
-
if (!attribute || !isNodeOfType(attribute, "JSXAttribute")) continue;
|
|
9301
|
-
if (getJsxAttributeName(attribute.name)?.toLowerCase() === targetPropLower) return attribute;
|
|
9302
|
-
}
|
|
9303
|
-
};
|
|
9304
|
-
const getStaticNativeTitleArrayValue = (expression, scopes) => {
|
|
9305
|
-
const elementValues = [];
|
|
9306
|
-
for (const rawElement of expression.elements) {
|
|
9307
|
-
if (rawElement === null) {
|
|
9308
|
-
elementValues.push("");
|
|
9309
|
-
continue;
|
|
9310
|
-
}
|
|
9311
|
-
if (isNodeOfType(rawElement, "SpreadElement")) return null;
|
|
9312
|
-
const element = stripParenExpression(rawElement);
|
|
9313
|
-
if (isNodeOfType(element, "Literal")) {
|
|
9314
|
-
elementValues.push(element.value === null ? "" : String(element.value));
|
|
9315
|
-
continue;
|
|
9316
|
-
}
|
|
9317
|
-
if (isNodeOfType(element, "TemplateLiteral")) {
|
|
9318
|
-
const staticValue = getStaticTemplateLiteralValue(element);
|
|
9319
|
-
if (staticValue === null) return null;
|
|
9320
|
-
elementValues.push(staticValue);
|
|
9321
|
-
continue;
|
|
9322
|
-
}
|
|
9323
|
-
if (isNodeOfType(element, "Identifier") && element.name === "undefined" && scopes.isGlobalReference(element) || isNodeOfType(element, "UnaryExpression") && element.operator === "void") {
|
|
9324
|
-
elementValues.push("");
|
|
9325
|
-
continue;
|
|
9326
|
-
}
|
|
9327
|
-
if (isNodeOfType(element, "ArrayExpression")) {
|
|
9328
|
-
const nestedValue = getStaticNativeTitleArrayValue(element, scopes);
|
|
9329
|
-
if (nestedValue === null) return null;
|
|
9330
|
-
elementValues.push(nestedValue);
|
|
9331
|
-
continue;
|
|
9332
|
-
}
|
|
9333
|
-
return null;
|
|
9334
|
-
}
|
|
9335
|
-
return elementValues.join(",");
|
|
9336
|
-
};
|
|
9337
|
-
const isGlobalSymbolExpression = (expression, scopes) => {
|
|
9338
|
-
if (isNodeOfType(expression, "Identifier")) return expression.name === "Symbol" && scopes.isGlobalReference(expression);
|
|
9339
|
-
if (!isNodeOfType(expression, "MemberExpression")) return false;
|
|
9340
|
-
const object = stripParenExpression(expression.object);
|
|
9341
|
-
return isNodeOfType(object, "Identifier") && object.name === "Symbol" && scopes.isGlobalReference(object);
|
|
9342
|
-
};
|
|
9343
|
-
const hasNonEmptyNativeTitleExpression = (rawExpression, scopes) => {
|
|
9344
|
-
const expression = stripParenExpression(rawExpression);
|
|
9345
|
-
if (isNodeOfType(expression, "Literal")) {
|
|
9346
|
-
if (typeof expression.value === "string") return expression.value.trim().length > 0;
|
|
9347
|
-
return expression.value !== null && typeof expression.value !== "boolean";
|
|
9348
|
-
}
|
|
9349
|
-
if (isNodeOfType(expression, "TemplateLiteral")) {
|
|
9350
|
-
const staticValue = getStaticTemplateLiteralValue(expression);
|
|
9351
|
-
return staticValue === null || staticValue.trim().length > 0;
|
|
9352
|
-
}
|
|
9353
|
-
if (isNodeOfType(expression, "Identifier")) return expression.name !== "undefined" || !scopes.isGlobalReference(expression);
|
|
9354
|
-
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return false;
|
|
9355
|
-
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return false;
|
|
9356
|
-
if (isNodeOfType(expression, "ArrowFunctionExpression")) return false;
|
|
9357
|
-
if (isNodeOfType(expression, "FunctionExpression")) return false;
|
|
9358
|
-
if (isNodeOfType(expression, "ClassExpression")) return false;
|
|
9359
|
-
if (isNodeOfType(expression, "ArrayExpression")) {
|
|
9360
|
-
const staticValue = getStaticNativeTitleArrayValue(expression, scopes);
|
|
9361
|
-
return staticValue === null || staticValue.trim().length > 0;
|
|
9362
|
-
}
|
|
9363
|
-
if (isGlobalSymbolExpression(expression, scopes)) return false;
|
|
9364
|
-
if (isNodeOfType(expression, "CallExpression") && isGlobalSymbolExpression(expression.callee, scopes)) return false;
|
|
9365
|
-
if (isNodeOfType(expression, "ConditionalExpression")) return hasNonEmptyNativeTitleExpression(expression.consequent, scopes) && hasNonEmptyNativeTitleExpression(expression.alternate, scopes);
|
|
9366
|
-
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
9367
|
-
const finalExpression = expression.expressions.at(-1);
|
|
9368
|
-
return finalExpression ? hasNonEmptyNativeTitleExpression(finalExpression, scopes) : false;
|
|
9369
|
-
}
|
|
9370
|
-
if (isNodeOfType(expression, "LogicalExpression")) {
|
|
9371
|
-
const leftExpression = stripParenExpression(expression.left);
|
|
9372
|
-
if (!isNodeOfType(leftExpression, "Literal")) return expression.operator !== "&&";
|
|
9373
|
-
if (expression.operator === "??") return hasNonEmptyNativeTitleExpression(leftExpression.value === null ? expression.right : leftExpression, scopes);
|
|
9374
|
-
const leftValueIsTruthy = Boolean(leftExpression.value);
|
|
9375
|
-
if (expression.operator === "&&") return hasNonEmptyNativeTitleExpression(leftValueIsTruthy ? expression.right : leftExpression, scopes);
|
|
9376
|
-
return hasNonEmptyNativeTitleExpression(leftValueIsTruthy ? leftExpression : expression.right, scopes);
|
|
9377
|
-
}
|
|
9378
|
-
return true;
|
|
9379
|
-
};
|
|
9380
|
-
const hasNonEmptyNativeTitle = (attribute, scopes) => {
|
|
9381
|
-
if (!attribute?.value) return false;
|
|
9382
|
-
if (isNodeOfType(attribute.value, "Literal")) return typeof attribute.value.value === "string" && attribute.value.value.trim().length > 0;
|
|
9383
|
-
if (isNodeOfType(attribute.value, "JSXExpressionContainer")) return hasNonEmptyNativeTitleExpression(attribute.value.expression, scopes);
|
|
9384
|
-
return true;
|
|
9385
|
-
};
|
|
9386
8893
|
const toAttributeMatchKey = (kind, value) => {
|
|
9387
8894
|
const trimmedValue = value.trim();
|
|
9388
8895
|
return trimmedValue.length > 0 ? `${kind}:${trimmedValue}` : null;
|
|
@@ -9639,7 +9146,6 @@ const controlHasAssociatedLabel = defineRule({
|
|
|
9639
9146
|
if (typeValue === "submit" || typeValue === "reset") return;
|
|
9640
9147
|
if (typeValue === "button" && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "value"))) return;
|
|
9641
9148
|
}
|
|
9642
|
-
if (isDomElement && hasNonEmptyNativeTitle(getLastJsxPropIgnoreCase(opening.attributes, TITLE_ATTRIBUTE), context.scopes)) return;
|
|
9643
9149
|
if (supportsPlaceholderNameFallback(tagName, opening) && hasNonEmptyPropValue(hasJsxPropIgnoreCase(opening.attributes, "placeholder"))) return;
|
|
9644
9150
|
if (hasLabellingProp(opening.attributes, settings.labelAttributes)) return;
|
|
9645
9151
|
if (isInsideJsxAttribute(node)) return;
|
|
@@ -11589,7 +11095,7 @@ const componentOrHookDisplayNameForFunction = (functionNode) => {
|
|
|
11589
11095
|
//#endregion
|
|
11590
11096
|
//#region src/plugin/utils/enclosing-component-or-hook-name.ts
|
|
11591
11097
|
const enclosingComponentOrHookName = (node) => {
|
|
11592
|
-
const functionNode = findEnclosingFunction
|
|
11098
|
+
const functionNode = findEnclosingFunction(node);
|
|
11593
11099
|
return functionNode ? componentOrHookDisplayNameForFunction(functionNode) : null;
|
|
11594
11100
|
};
|
|
11595
11101
|
//#endregion
|
|
@@ -11646,11 +11152,11 @@ const executesDuringRender = (functionNode, scopes) => {
|
|
|
11646
11152
|
//#endregion
|
|
11647
11153
|
//#region src/plugin/utils/find-render-phase-component-or-hook.ts
|
|
11648
11154
|
const findRenderPhaseComponentOrHook = (node, scopes) => {
|
|
11649
|
-
let functionNode = findEnclosingFunction
|
|
11155
|
+
let functionNode = findEnclosingFunction(node);
|
|
11650
11156
|
while (functionNode) {
|
|
11651
11157
|
if (componentOrHookDisplayNameForFunction(functionNode)) return functionNode;
|
|
11652
11158
|
if (!executesDuringRender(functionNode, scopes)) return null;
|
|
11653
|
-
functionNode = findEnclosingFunction
|
|
11159
|
+
functionNode = findEnclosingFunction(functionNode);
|
|
11654
11160
|
}
|
|
11655
11161
|
return null;
|
|
11656
11162
|
};
|
|
@@ -11899,13 +11405,13 @@ const findSubscribeLikeUsages = (callback, context) => {
|
|
|
11899
11405
|
};
|
|
11900
11406
|
const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, context) => {
|
|
11901
11407
|
let pathAnchor = usageNode;
|
|
11902
|
-
let pathOwner = findEnclosingFunction
|
|
11408
|
+
let pathOwner = findEnclosingFunction(pathAnchor);
|
|
11903
11409
|
while (pathOwner && isSynchronousIteratorCallback(pathOwner)) {
|
|
11904
11410
|
if (matchingNodes.length > 0 && matchingNodes.every((matchingNode) => context.cfg.enclosingFunction(matchingNode) === pathOwner)) break;
|
|
11905
11411
|
const iteratorCall = pathOwner.parent;
|
|
11906
11412
|
if (!isNodeOfType(iteratorCall, "CallExpression")) break;
|
|
11907
11413
|
pathAnchor = iteratorCall;
|
|
11908
|
-
pathOwner = findEnclosingFunction
|
|
11414
|
+
pathOwner = findEnclosingFunction(pathAnchor);
|
|
11909
11415
|
}
|
|
11910
11416
|
const owner = context.cfg.enclosingFunction(pathAnchor);
|
|
11911
11417
|
if (!owner) return false;
|
|
@@ -12134,7 +11640,7 @@ const findCollectionMappingCall = (callbackNode) => {
|
|
|
12134
11640
|
return callee.property.name === "map" && callNode.arguments?.[0] === callbackNode ? callNode : null;
|
|
12135
11641
|
};
|
|
12136
11642
|
const findMappedResourceCollectionKey = (resourceNode, context) => {
|
|
12137
|
-
const callbackNode = findEnclosingFunction
|
|
11643
|
+
const callbackNode = findEnclosingFunction(resourceNode);
|
|
12138
11644
|
if (!callbackNode || !isNodeOfType(callbackNode, "ArrowFunctionExpression") && !isNodeOfType(callbackNode, "FunctionExpression")) return null;
|
|
12139
11645
|
const mappingCall = findCollectionMappingCall(callbackNode);
|
|
12140
11646
|
if (!mappingCall) return null;
|
|
@@ -12245,10 +11751,10 @@ const findSingleDirectInvocation = (functionNode, caller, context) => {
|
|
|
12245
11751
|
});
|
|
12246
11752
|
if (invocationCalls.length !== 1) return null;
|
|
12247
11753
|
const invocationCall = invocationCalls[0];
|
|
12248
|
-
return findEnclosingFunction
|
|
11754
|
+
return findEnclosingFunction(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
|
|
12249
11755
|
};
|
|
12250
11756
|
const resolveCleanupPathAnchor = (usageNode, effectCallback, context) => {
|
|
12251
|
-
const usageFunction = findEnclosingFunction
|
|
11757
|
+
const usageFunction = findEnclosingFunction(usageNode);
|
|
12252
11758
|
if (!usageFunction || usageFunction === effectCallback) return usageNode;
|
|
12253
11759
|
return findSingleDirectInvocation(usageFunction, effectCallback, context) ?? usageNode;
|
|
12254
11760
|
};
|
|
@@ -12263,7 +11769,7 @@ const resolveSingleAssignedCleanupFunction = (expression, usage, context) => {
|
|
|
12263
11769
|
const assignmentReference = assignmentReferences[0];
|
|
12264
11770
|
const assignmentTarget = findTransparentExpressionRoot(assignmentReference.identifier);
|
|
12265
11771
|
const assignmentNode = assignmentTarget.parent;
|
|
12266
|
-
if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction
|
|
11772
|
+
if (!isNodeOfType(assignmentNode, "AssignmentExpression") || assignmentNode.operator !== "=" || assignmentNode.left !== assignmentTarget || findEnclosingFunction(assignmentNode) !== findEnclosingFunction(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [assignmentNode], context)) return null;
|
|
12267
11773
|
const assignedValue = stripParenExpression(assignmentNode.right);
|
|
12268
11774
|
return isFunctionLike$2(assignedValue) ? assignedValue : null;
|
|
12269
11775
|
};
|
|
@@ -12352,12 +11858,12 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
|
|
|
12352
11858
|
return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
|
|
12353
11859
|
};
|
|
12354
11860
|
const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
|
|
12355
|
-
const componentFunction = findEnclosingFunction
|
|
11861
|
+
const componentFunction = findEnclosingFunction(callback);
|
|
12356
11862
|
if (!componentFunction || !isNodeOfType(componentFunction, "ArrowFunctionExpression") && !isNodeOfType(componentFunction, "FunctionExpression") && !isNodeOfType(componentFunction, "FunctionDeclaration")) return false;
|
|
12357
11863
|
let didFindUnmountCleanup = false;
|
|
12358
11864
|
walkAst(componentFunction.body, (child) => {
|
|
12359
11865
|
if (didFindUnmountCleanup) return false;
|
|
12360
|
-
if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction
|
|
11866
|
+
if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction) return;
|
|
12361
11867
|
if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
|
|
12362
11868
|
const dependencyList = child.arguments?.[1];
|
|
12363
11869
|
if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
|
|
@@ -12453,7 +11959,7 @@ const deferredUsageWritesGuardBeforeUsage = (callback, usageNode, guardState, co
|
|
|
12453
11959
|
return didWriteGuard;
|
|
12454
11960
|
};
|
|
12455
11961
|
const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
|
|
12456
|
-
const usageFunction = findEnclosingFunction
|
|
11962
|
+
const usageFunction = findEnclosingFunction(usage.node);
|
|
12457
11963
|
const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
|
|
12458
11964
|
if (usage.handleKey === null || !usageFunction || usageFunction === callback || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
|
|
12459
11965
|
return collectDeferredUsageGuardStates(usageFunction, usage.node, context).some((guardState) => !deferredUsageWritesGuardBeforeUsage(usageFunction, usage.node, guardState, context) && cleanupReturns.every((cleanupReturn) => cleanupReturnInvalidatesGuard(cleanupReturn, guardState, context)));
|
|
@@ -12461,7 +11967,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
|
|
|
12461
11967
|
const effectHasCleanupForUsage = (callback, usage, context) => {
|
|
12462
11968
|
if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
|
|
12463
11969
|
if (callback.async) return false;
|
|
12464
|
-
if (usage.kind === "subscribe" && findEnclosingFunction
|
|
11970
|
+
if (usage.kind === "subscribe" && findEnclosingFunction(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
|
|
12465
11971
|
if (!isNodeOfType(callback.body, "BlockStatement")) return callback.body === usage.node && isCleanupReturningSubscribeLikeCallExpression(callback.body);
|
|
12466
11972
|
const matchingCleanupReturns = [];
|
|
12467
11973
|
walkInsideStatementBlocks(callback.body, (child) => {
|
|
@@ -12630,7 +12136,7 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
|
|
|
12630
12136
|
parentNode = currentNode.parent;
|
|
12631
12137
|
}
|
|
12632
12138
|
if (!isNodeOfType(parentNode, "ReturnStatement") || parentNode.argument !== currentNode) return false;
|
|
12633
|
-
const effectCallback = findEnclosingFunction
|
|
12139
|
+
const effectCallback = findEnclosingFunction(parentNode);
|
|
12634
12140
|
const effectCall = effectCallback?.parent;
|
|
12635
12141
|
return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
|
|
12636
12142
|
};
|
|
@@ -12640,13 +12146,13 @@ const isPotentiallyReachableFunction = (functionNode, context) => {
|
|
|
12640
12146
|
if (!bindingIdentifier) return false;
|
|
12641
12147
|
const symbol = context.scopes.symbolFor(bindingIdentifier);
|
|
12642
12148
|
if (!symbol) return false;
|
|
12643
|
-
return symbol.references.some((reference) => findEnclosingFunction
|
|
12149
|
+
return symbol.references.some((reference) => findEnclosingFunction(reference.identifier) !== functionNode);
|
|
12644
12150
|
};
|
|
12645
12151
|
const isReleaseReachableForUsage = (releaseNode, usage, context) => {
|
|
12646
12152
|
if (!isNodeReachableWithinFunction(releaseNode, context)) return false;
|
|
12647
|
-
const releaseFunction = findEnclosingFunction
|
|
12153
|
+
const releaseFunction = findEnclosingFunction(releaseNode);
|
|
12648
12154
|
if (!releaseFunction) return true;
|
|
12649
|
-
if (releaseFunction === findEnclosingFunction
|
|
12155
|
+
if (releaseFunction === findEnclosingFunction(usage.node)) return true;
|
|
12650
12156
|
return isPotentiallyReachableFunction(releaseFunction, context);
|
|
12651
12157
|
};
|
|
12652
12158
|
const fileContainsReleaseForUsage = (usage, context) => {
|
|
@@ -12829,10 +12335,10 @@ const cleanupFunctionReleasesRefOwnedUsage = (cleanupFunction, componentFunction
|
|
|
12829
12335
|
}
|
|
12830
12336
|
if (assignedKey !== storage.refCurrentKey) return;
|
|
12831
12337
|
const assignedValue = stripParenExpression(child.right);
|
|
12832
|
-
if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction
|
|
12338
|
+
if (isNodeOfType(assignedValue, "Literal") && assignedValue.value === null && findEnclosingFunction(child) === cleanupFunction) return;
|
|
12833
12339
|
const assignedSessionProperties = (isNodeOfType(assignedValue, "ObjectExpression") ? assignedValue : null)?.properties ?? [];
|
|
12834
12340
|
const storesMatchingHandler = assignedSessionProperties.every((property) => isNodeOfType(property, "Property")) && assignedSessionProperties.some((property) => isNodeOfType(property, "Property") && `${storage.refCurrentKey}.${getStaticPropertyKeyName(property) ?? ""}` === storage.handlerKey && resolveExpressionKey(property.value, context) === usage.handlerKey);
|
|
12835
|
-
if (findEnclosingFunction
|
|
12341
|
+
if (findEnclosingFunction(child) !== retainedFunction || !storesMatchingHandler) {
|
|
12836
12342
|
hasUnsafeRefWrite = true;
|
|
12837
12343
|
return false;
|
|
12838
12344
|
}
|
|
@@ -12853,12 +12359,12 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
|
|
|
12853
12359
|
return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
|
|
12854
12360
|
};
|
|
12855
12361
|
const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
|
|
12856
|
-
const componentFunction = findEnclosingFunction
|
|
12362
|
+
const componentFunction = findEnclosingFunction(retainedFunction);
|
|
12857
12363
|
if (!componentFunction || !isFunctionLike$2(componentFunction)) return false;
|
|
12858
12364
|
let didFindCleanupEffect = false;
|
|
12859
12365
|
walkAst(componentFunction.body, (child) => {
|
|
12860
12366
|
if (didFindCleanupEffect) return false;
|
|
12861
|
-
if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction
|
|
12367
|
+
if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction || !isReactApiCall(child, "useEffect", context.scopes)) return;
|
|
12862
12368
|
const effectStatement = findTransparentExpressionRoot(child).parent;
|
|
12863
12369
|
if (!isNodeOfType(effectStatement, "ExpressionStatement") || effectStatement.parent !== componentFunction.body) return;
|
|
12864
12370
|
const effectCallback = getEffectCallback(child);
|
|
@@ -13712,202 +13218,6 @@ const resolveExhaustiveDepsSettings = (settings) => {
|
|
|
13712
13218
|
};
|
|
13713
13219
|
};
|
|
13714
13220
|
//#endregion
|
|
13715
|
-
//#region src/plugin/utils/is-ast-descendant.ts
|
|
13716
|
-
/**
|
|
13717
|
-
* True when `inner` is `outer` itself or any descendant in the AST
|
|
13718
|
-
* `parent` chain. Walks `inner.parent` upward and stops at either a
|
|
13719
|
-
* match (`true`) or the chain's root (`false`).
|
|
13720
|
-
*
|
|
13721
|
-
* Was duplicated byte-identical in:
|
|
13722
|
-
* - exhaustive-deps-symbol-stability.ts
|
|
13723
|
-
* - semantic/closure-captures.ts
|
|
13724
|
-
*/
|
|
13725
|
-
const isAstDescendant = (inner, outer) => {
|
|
13726
|
-
let current = inner;
|
|
13727
|
-
while (current) {
|
|
13728
|
-
if (current === outer) return true;
|
|
13729
|
-
current = current.parent ?? null;
|
|
13730
|
-
}
|
|
13731
|
-
return false;
|
|
13732
|
-
};
|
|
13733
|
-
//#endregion
|
|
13734
|
-
//#region src/plugin/rules/react-builtins/exhaustive-deps-sole-writer-guard.ts
|
|
13735
|
-
const EQUALITY_BINARY_OPERATORS = new Set(["===", "!=="]);
|
|
13736
|
-
const getUseStateSetterSymbol = (stateSymbol, scopes) => {
|
|
13737
|
-
const declaration = stateSymbol.declarationNode;
|
|
13738
|
-
if (!isNodeOfType(declaration, "VariableDeclarator")) return null;
|
|
13739
|
-
if (!isNodeOfType(declaration.id, "ArrayPattern")) return null;
|
|
13740
|
-
const stateBinding = declaration.id.elements[0];
|
|
13741
|
-
const setterBinding = declaration.id.elements[1];
|
|
13742
|
-
if (stateBinding !== stateSymbol.bindingIdentifier || !isNodeOfType(setterBinding, "Identifier")) return null;
|
|
13743
|
-
const initializer = declaration.init ? stripParenExpression(declaration.init) : null;
|
|
13744
|
-
if (!initializer || !isReactApiCall(initializer, "useState", scopes, {
|
|
13745
|
-
allowGlobalReactNamespace: true,
|
|
13746
|
-
allowUnboundBareCalls: true,
|
|
13747
|
-
resolveNamedAliases: true
|
|
13748
|
-
})) return null;
|
|
13749
|
-
return scopes.symbolFor(setterBinding);
|
|
13750
|
-
};
|
|
13751
|
-
const getDirectSetterCall = (setterSymbol, callback) => {
|
|
13752
|
-
if (setterSymbol.references.length !== 1) return null;
|
|
13753
|
-
const setterReference = setterSymbol.references[0];
|
|
13754
|
-
if (setterReference.flag !== "read") return null;
|
|
13755
|
-
const referenceRoot = findTransparentExpressionRoot(setterReference.identifier);
|
|
13756
|
-
const callExpression = referenceRoot.parent;
|
|
13757
|
-
if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== referenceRoot || callExpression.arguments.length !== 1 || findEnclosingFunction$1(callExpression) !== callback) return null;
|
|
13758
|
-
const writtenValue = stripParenExpression(callExpression.arguments[0]);
|
|
13759
|
-
if (isNodeOfType(writtenValue, "ArrowFunctionExpression") || isNodeOfType(writtenValue, "FunctionExpression") || isNodeOfType(writtenValue, "SpreadElement")) return null;
|
|
13760
|
-
return {
|
|
13761
|
-
callExpression,
|
|
13762
|
-
writtenValue
|
|
13763
|
-
};
|
|
13764
|
-
};
|
|
13765
|
-
const isGlobalObjectIsCall = (callExpression, scopes) => {
|
|
13766
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return false;
|
|
13767
|
-
const callee = stripParenExpression(callExpression.callee);
|
|
13768
|
-
if (!isNodeOfType(callee, "MemberExpression")) return false;
|
|
13769
|
-
const propertyName = getStaticPropertyName(callee);
|
|
13770
|
-
const receiver = stripParenExpression(callee.object);
|
|
13771
|
-
return Boolean(propertyName === "is" && isNodeOfType(receiver, "Identifier") && receiver.name === "Object" && scopes.isGlobalReference(receiver));
|
|
13772
|
-
};
|
|
13773
|
-
const isGlobalNaNReference = (expression, scopes) => {
|
|
13774
|
-
const unwrappedExpression = stripParenExpression(expression);
|
|
13775
|
-
return isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression);
|
|
13776
|
-
};
|
|
13777
|
-
const getEqualityComparison = (stateReference, candidate, scopes) => {
|
|
13778
|
-
const unwrappedStateReference = stripParenExpression(stateReference);
|
|
13779
|
-
if (isNodeOfType(candidate, "BinaryExpression") && EQUALITY_BINARY_OPERATORS.has(candidate.operator)) {
|
|
13780
|
-
if (stripParenExpression(candidate.left) === unwrappedStateReference) return isGlobalNaNReference(candidate.right, scopes) ? null : {
|
|
13781
|
-
comparison: candidate,
|
|
13782
|
-
counterpart: candidate.right,
|
|
13783
|
-
areValuesEqualWhenTruthy: candidate.operator === "==="
|
|
13784
|
-
};
|
|
13785
|
-
if (stripParenExpression(candidate.right) === unwrappedStateReference) return isGlobalNaNReference(candidate.left, scopes) ? null : {
|
|
13786
|
-
comparison: candidate,
|
|
13787
|
-
counterpart: candidate.left,
|
|
13788
|
-
areValuesEqualWhenTruthy: candidate.operator === "==="
|
|
13789
|
-
};
|
|
13790
|
-
return null;
|
|
13791
|
-
}
|
|
13792
|
-
if (!isNodeOfType(candidate, "CallExpression") || !isGlobalObjectIsCall(candidate, scopes) || candidate.arguments.length !== 2) return null;
|
|
13793
|
-
const firstArgument = candidate.arguments[0];
|
|
13794
|
-
const secondArgument = candidate.arguments[1];
|
|
13795
|
-
if (isNodeOfType(firstArgument, "SpreadElement") || isNodeOfType(secondArgument, "SpreadElement")) return null;
|
|
13796
|
-
if (stripParenExpression(firstArgument) === unwrappedStateReference) return {
|
|
13797
|
-
comparison: candidate,
|
|
13798
|
-
counterpart: secondArgument,
|
|
13799
|
-
areValuesEqualWhenTruthy: true
|
|
13800
|
-
};
|
|
13801
|
-
if (stripParenExpression(secondArgument) === unwrappedStateReference) return {
|
|
13802
|
-
comparison: candidate,
|
|
13803
|
-
counterpart: firstArgument,
|
|
13804
|
-
areValuesEqualWhenTruthy: true
|
|
13805
|
-
};
|
|
13806
|
-
return null;
|
|
13807
|
-
};
|
|
13808
|
-
const findEqualityComparison = (stateReference, test, scopes) => {
|
|
13809
|
-
let current = stateReference.parent;
|
|
13810
|
-
while (current && isAstDescendant(current, test)) {
|
|
13811
|
-
const comparison = getEqualityComparison(stateReference, current, scopes);
|
|
13812
|
-
if (comparison) return comparison;
|
|
13813
|
-
if (current === test) break;
|
|
13814
|
-
current = current.parent;
|
|
13815
|
-
}
|
|
13816
|
-
return null;
|
|
13817
|
-
};
|
|
13818
|
-
const doesTestOutcomeRequireComparisonOutcome = (comparison, test, testOutcome, comparisonOutcome) => {
|
|
13819
|
-
let requiredChildOutcome = testOutcome;
|
|
13820
|
-
let current = comparison;
|
|
13821
|
-
while (current !== test) {
|
|
13822
|
-
const parent = current.parent;
|
|
13823
|
-
if (!parent || !isAstDescendant(parent, test)) return false;
|
|
13824
|
-
if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") requiredChildOutcome = !requiredChildOutcome;
|
|
13825
|
-
else if (isNodeOfType(parent, "LogicalExpression")) {
|
|
13826
|
-
if (parent.operator === "&&" && !requiredChildOutcome) return false;
|
|
13827
|
-
if (parent.operator === "||" && requiredChildOutcome) return false;
|
|
13828
|
-
if (parent.operator !== "&&" && parent.operator !== "||") return false;
|
|
13829
|
-
} else if (!TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type)) return false;
|
|
13830
|
-
current = parent;
|
|
13831
|
-
}
|
|
13832
|
-
return requiredChildOutcome === comparisonOutcome;
|
|
13833
|
-
};
|
|
13834
|
-
const resolveImmutableAliasExpression = (expression, scopes) => {
|
|
13835
|
-
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
13836
|
-
let current = stripParenExpression(expression);
|
|
13837
|
-
while (isNodeOfType(current, "Identifier")) {
|
|
13838
|
-
const symbol = scopes.symbolFor(current);
|
|
13839
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) break;
|
|
13840
|
-
const initializer = getDirectConstInitializer(symbol);
|
|
13841
|
-
if (!initializer || !isNodeOfType(stripParenExpression(initializer), "Identifier")) break;
|
|
13842
|
-
visitedSymbolIds.add(symbol.id);
|
|
13843
|
-
current = stripParenExpression(initializer);
|
|
13844
|
-
}
|
|
13845
|
-
return current;
|
|
13846
|
-
};
|
|
13847
|
-
const referencesSameValue = (leftExpression, rightExpression, scopes) => {
|
|
13848
|
-
const left = resolveImmutableAliasExpression(leftExpression, scopes);
|
|
13849
|
-
const right = resolveImmutableAliasExpression(rightExpression, scopes);
|
|
13850
|
-
if (left === right) return true;
|
|
13851
|
-
if (isNodeOfType(left, "Identifier") && isNodeOfType(right, "Identifier")) {
|
|
13852
|
-
const leftSymbol = scopes.symbolFor(left);
|
|
13853
|
-
const rightSymbol = scopes.symbolFor(right);
|
|
13854
|
-
if (leftSymbol || rightSymbol) return leftSymbol?.id === rightSymbol?.id;
|
|
13855
|
-
return left.name === right.name;
|
|
13856
|
-
}
|
|
13857
|
-
if (isNodeOfType(left, "Literal") && isNodeOfType(right, "Literal")) return Object.is(left.value, right.value);
|
|
13858
|
-
return false;
|
|
13859
|
-
};
|
|
13860
|
-
const hasDeclaredTriggerDependency = (callback) => {
|
|
13861
|
-
const hookCall = findTransparentExpressionRoot(callback).parent;
|
|
13862
|
-
if (!isNodeOfType(hookCall, "CallExpression")) return false;
|
|
13863
|
-
const dependencyArray = hookCall.arguments[1];
|
|
13864
|
-
return Boolean(isNodeOfType(dependencyArray, "ArrayExpression") && dependencyArray.elements.length);
|
|
13865
|
-
};
|
|
13866
|
-
const doesBranchExitEffect = (branch) => {
|
|
13867
|
-
if (isNodeOfType(branch, "ReturnStatement") || isNodeOfType(branch, "ThrowStatement")) return true;
|
|
13868
|
-
if (!isNodeOfType(branch, "BlockStatement")) return false;
|
|
13869
|
-
const terminalStatement = branch.body.at(-1);
|
|
13870
|
-
return Boolean(terminalStatement && doesBranchExitEffect(terminalStatement));
|
|
13871
|
-
};
|
|
13872
|
-
const doesGuardDominateLaterSetter = (guard, setterCall) => {
|
|
13873
|
-
if (!isNodeOfType(guard, "IfStatement") || guard.alternate) return false;
|
|
13874
|
-
if (!doesBranchExitEffect(guard.consequent)) return false;
|
|
13875
|
-
const block = guard.parent;
|
|
13876
|
-
if (!isNodeOfType(block, "BlockStatement")) return false;
|
|
13877
|
-
const guardIndex = block.body.findIndex((statement) => statement === guard);
|
|
13878
|
-
return block.body.some((statement, statementIndex) => statementIndex > guardIndex && isAstDescendant(setterCall, statement));
|
|
13879
|
-
};
|
|
13880
|
-
const findDominatingGuardCounterpart = (stateReference, setterCall, callback, scopes) => {
|
|
13881
|
-
let current = stateReference.parent;
|
|
13882
|
-
while (current && current !== callback) {
|
|
13883
|
-
if (isNodeOfType(current, "IfStatement") && isAstDescendant(stateReference, current.test) && (isAstDescendant(setterCall, current.consequent) || Boolean(current.alternate && isAstDescendant(setterCall, current.alternate)) || doesGuardDominateLaterSetter(current, setterCall))) {
|
|
13884
|
-
const setterRunsWhenTestTruthy = isAstDescendant(setterCall, current.consequent);
|
|
13885
|
-
if (setterRunsWhenTestTruthy === (Boolean(current.alternate && isAstDescendant(setterCall, current.alternate)) || doesGuardDominateLaterSetter(current, setterCall))) return null;
|
|
13886
|
-
const equalityComparison = findEqualityComparison(stateReference, current.test, scopes);
|
|
13887
|
-
if (!equalityComparison) return null;
|
|
13888
|
-
const comparisonOutcomeForDifferentValues = !equalityComparison.areValuesEqualWhenTruthy;
|
|
13889
|
-
if (!doesTestOutcomeRequireComparisonOutcome(equalityComparison.comparison, current.test, setterRunsWhenTestTruthy, comparisonOutcomeForDifferentValues)) return null;
|
|
13890
|
-
return equalityComparison.counterpart;
|
|
13891
|
-
}
|
|
13892
|
-
current = current.parent;
|
|
13893
|
-
}
|
|
13894
|
-
return null;
|
|
13895
|
-
};
|
|
13896
|
-
const isSoleWriterEffectGuardCapture = (stateSymbol, callback, scopes) => {
|
|
13897
|
-
if (!hasDeclaredTriggerDependency(callback)) return false;
|
|
13898
|
-
if (stateSymbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
13899
|
-
const setterSymbol = getUseStateSetterSymbol(stateSymbol, scopes);
|
|
13900
|
-
if (!setterSymbol) return false;
|
|
13901
|
-
const setterWrite = getDirectSetterCall(setterSymbol, callback);
|
|
13902
|
-
if (!setterWrite) return false;
|
|
13903
|
-
const callbackStateReferences = stateSymbol.references.filter((reference) => isAstDescendant(reference.identifier, callback));
|
|
13904
|
-
if (callbackStateReferences.length !== 1) return false;
|
|
13905
|
-
const stateReferenceRoot = findTransparentExpressionRoot(callbackStateReferences[0].identifier);
|
|
13906
|
-
if (isNodeOfType(stateReferenceRoot.parent, "MemberExpression") && stateReferenceRoot.parent.object === stateReferenceRoot) return false;
|
|
13907
|
-
const counterpart = findDominatingGuardCounterpart(callbackStateReferences[0].identifier, setterWrite.callExpression, callback, scopes);
|
|
13908
|
-
return Boolean(counterpart && referencesSameValue(counterpart, setterWrite.writtenValue, scopes));
|
|
13909
|
-
};
|
|
13910
|
-
//#endregion
|
|
13911
13221
|
//#region src/plugin/rules/react-builtins/exhaustive-deps-suppression.ts
|
|
13912
13222
|
const DISABLE_COMMENT_RULE_NAME_PATTERN$1 = /(?:^|[\s,/])exhaustive-deps(?:$|[\s,:])/;
|
|
13913
13223
|
const DISABLE_NEXT_LINE_PATTERN$1 = /\b(?:eslint|oxlint)-disable-next-line\b([^\n]*)/;
|
|
@@ -13979,6 +13289,25 @@ const isExhaustiveDepsSuppressedAt = (filename, nodeStartOffset) => {
|
|
|
13979
13289
|
return index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf16NewlineOffsets)) || index.suppressedLines.has(lineForOffset$1(nodeStartOffset, index.utf8NewlineOffsets));
|
|
13980
13290
|
};
|
|
13981
13291
|
//#endregion
|
|
13292
|
+
//#region src/plugin/utils/is-ast-descendant.ts
|
|
13293
|
+
/**
|
|
13294
|
+
* True when `inner` is `outer` itself or any descendant in the AST
|
|
13295
|
+
* `parent` chain. Walks `inner.parent` upward and stops at either a
|
|
13296
|
+
* match (`true`) or the chain's root (`false`).
|
|
13297
|
+
*
|
|
13298
|
+
* Was duplicated byte-identical in:
|
|
13299
|
+
* - exhaustive-deps-symbol-stability.ts
|
|
13300
|
+
* - semantic/closure-captures.ts
|
|
13301
|
+
*/
|
|
13302
|
+
const isAstDescendant = (inner, outer) => {
|
|
13303
|
+
let current = inner;
|
|
13304
|
+
while (current) {
|
|
13305
|
+
if (current === outer) return true;
|
|
13306
|
+
current = current.parent ?? null;
|
|
13307
|
+
}
|
|
13308
|
+
return false;
|
|
13309
|
+
};
|
|
13310
|
+
//#endregion
|
|
13982
13311
|
//#region src/plugin/rules/react-builtins/exhaustive-deps-symbol-stability.ts
|
|
13983
13312
|
/**
|
|
13984
13313
|
* Symbol-stability helpers consumed by the `exhaustive-deps` rule.
|
|
@@ -14159,7 +13488,6 @@ const EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS = new Set([
|
|
|
14159
13488
|
"useLayoutEffect",
|
|
14160
13489
|
"useInsertionEffect"
|
|
14161
13490
|
]);
|
|
14162
|
-
const SOLE_WRITER_GUARD_HOOKS = new Set(["useEffect", "useLayoutEffect"]);
|
|
14163
13491
|
const buildAdditionalHooksRegex = (additional) => {
|
|
14164
13492
|
if (!additional) return null;
|
|
14165
13493
|
try {
|
|
@@ -14296,7 +13624,7 @@ const stringifyMemberChain = (node) => {
|
|
|
14296
13624
|
}
|
|
14297
13625
|
return null;
|
|
14298
13626
|
};
|
|
14299
|
-
const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys
|
|
13627
|
+
const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys) => {
|
|
14300
13628
|
const keys = /* @__PURE__ */ new Set();
|
|
14301
13629
|
const stableCapturedNames = /* @__PURE__ */ new Set();
|
|
14302
13630
|
const moduleScopeCapturedNames = /* @__PURE__ */ new Set();
|
|
@@ -14307,10 +13635,6 @@ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys, allow
|
|
|
14307
13635
|
const symbol = reference.resolvedSymbol;
|
|
14308
13636
|
if (!symbol) continue;
|
|
14309
13637
|
if (isRecursiveInitializerCapture(symbol, callback)) continue;
|
|
14310
|
-
if (allowSoleWriterEffectGuards && isSoleWriterEffectGuardCapture(symbol, callback, scopes)) {
|
|
14311
|
-
stableCapturedNames.add(symbol.name);
|
|
14312
|
-
continue;
|
|
14313
|
-
}
|
|
14314
13638
|
if (symbolHasStableValue(symbol, scopes)) {
|
|
14315
13639
|
stableCapturedNames.add(symbol.name);
|
|
14316
13640
|
continue;
|
|
@@ -14983,7 +14307,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
14983
14307
|
if (isNodeOfType(stripped, "Identifier")) declaredExactBindingKeys.add(key);
|
|
14984
14308
|
declaredKeyToReportNode.set(key, elementNode);
|
|
14985
14309
|
}
|
|
14986
|
-
const { keys: captureKeys, stableCapturedNames, moduleScopeCapturedNames, outerFunctionCapturedNames } = collectCaptureDepKeys(callbackToAnalyze ?? callbackArgument, context.scopes, declaredExactBindingKeys
|
|
14310
|
+
const { keys: captureKeys, stableCapturedNames, moduleScopeCapturedNames, outerFunctionCapturedNames } = collectCaptureDepKeys(callbackToAnalyze ?? callbackArgument, context.scopes, declaredExactBindingKeys);
|
|
14987
14311
|
for (const forcedCaptureKey of forcedCaptureKeys) captureKeys.add(forcedCaptureKey);
|
|
14988
14312
|
addAggregatePropsDependency(captureKeys, declaredKeys, callbackToAnalyze ?? callbackArgument, context.scopes);
|
|
14989
14313
|
const missingCaptureKeys = [];
|
|
@@ -16797,14 +16121,14 @@ const isBindingInvokedOnRenderPath = (root, bindingName) => {
|
|
|
16797
16121
|
if (didFindRenderPathInvocation) return false;
|
|
16798
16122
|
if (!isNodeOfType(node, "CallExpression")) return;
|
|
16799
16123
|
if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== bindingName) return;
|
|
16800
|
-
let enclosingFunction = findEnclosingFunction
|
|
16124
|
+
let enclosingFunction = findEnclosingFunction(node);
|
|
16801
16125
|
while (enclosingFunction) {
|
|
16802
16126
|
if (isDeferredCallbackPosition$1(enclosingFunction) || getHandlerNamedBindingName(enclosingFunction)) return;
|
|
16803
16127
|
if (containingFunctionIsComponentOrHook(enclosingFunction)) {
|
|
16804
16128
|
didFindRenderPathInvocation = true;
|
|
16805
16129
|
return;
|
|
16806
16130
|
}
|
|
16807
|
-
enclosingFunction = findEnclosingFunction
|
|
16131
|
+
enclosingFunction = findEnclosingFunction(enclosingFunction);
|
|
16808
16132
|
}
|
|
16809
16133
|
});
|
|
16810
16134
|
return didFindRenderPathInvocation;
|
|
@@ -16839,7 +16163,7 @@ const jotaiSelectAtomInRenderBody = defineRule({
|
|
|
16839
16163
|
};
|
|
16840
16164
|
return { CallExpression(node) {
|
|
16841
16165
|
if (!isImportedSelectAtom(node)) return;
|
|
16842
|
-
const nearestFunctionLike = findEnclosingFunction
|
|
16166
|
+
const nearestFunctionLike = findEnclosingFunction(node);
|
|
16843
16167
|
if (!nearestFunctionLike) return;
|
|
16844
16168
|
if (isDeferredCallback(nearestFunctionLike)) return;
|
|
16845
16169
|
if (isBindingUsedAsHandler(nearestFunctionLike)) return;
|
|
@@ -17836,7 +17160,7 @@ const isPersistentCacheSetWrite = (callExpression, newExpression, enclosingFunct
|
|
|
17836
17160
|
return !isAstDescendant(receiverBinding.scopeOwner, enclosingFunction);
|
|
17837
17161
|
};
|
|
17838
17162
|
const isInsideCacheMemo = (node) => {
|
|
17839
|
-
const enclosingFunction = findEnclosingFunction
|
|
17163
|
+
const enclosingFunction = findEnclosingFunction(node);
|
|
17840
17164
|
let child = node;
|
|
17841
17165
|
let cursor = node.parent ?? null;
|
|
17842
17166
|
while (cursor) {
|
|
@@ -17874,7 +17198,7 @@ const getFunctionName = (functionNode) => {
|
|
|
17874
17198
|
return null;
|
|
17875
17199
|
};
|
|
17876
17200
|
const isUncacheableOptionsMergeUtility = (node) => {
|
|
17877
|
-
const enclosingFunction = findEnclosingFunction
|
|
17201
|
+
const enclosingFunction = findEnclosingFunction(node);
|
|
17878
17202
|
if (!enclosingFunction || !isFunctionLike$2(enclosingFunction)) return false;
|
|
17879
17203
|
const functionName = getFunctionName(enclosingFunction);
|
|
17880
17204
|
if (!functionName || isComponentOrHookName(functionName)) return false;
|
|
@@ -18065,6 +17389,13 @@ const isSafeStatefulReplaceAllSearch = (node, flags, context) => {
|
|
|
18065
17389
|
const callee = stripParenExpression(replaceAllCall.callee);
|
|
18066
17390
|
return isNodeOfType(callee, "MemberExpression") && !callee.computed && !callee.optional && !replaceAllCall.optional && isNodeOfType(callee.property, "Identifier") && callee.property.name === "replaceAll" && isProvenNativeStringReceiver(callee.object, context);
|
|
18067
17391
|
};
|
|
17392
|
+
const getDestructuredBindingPropertyName = (bindingIdentifier) => {
|
|
17393
|
+
let bindingNode = bindingIdentifier;
|
|
17394
|
+
if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
|
|
17395
|
+
const property = bindingNode.parent;
|
|
17396
|
+
if (!isNodeOfType(property, "Property") || property.value !== bindingNode || !isNodeOfType(property.parent, "ObjectPattern")) return null;
|
|
17397
|
+
return getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
17398
|
+
};
|
|
18068
17399
|
const extendGlobalPath = (basePath, propertyName) => {
|
|
18069
17400
|
if (basePath === "global") {
|
|
18070
17401
|
if (propertyName === null || GLOBAL_OBJECT_NAMES.has(propertyName)) return "global";
|
|
@@ -18354,89 +17685,7 @@ const collectEarlierAndGuardOperands = (node) => {
|
|
|
18354
17685
|
return earlierOperands;
|
|
18355
17686
|
};
|
|
18356
17687
|
//#endregion
|
|
18357
|
-
//#region src/plugin/utils/unwrap-object-integrity-expression.ts
|
|
18358
|
-
const OBJECT_INTEGRITY_METHOD_NAMES = new Set([
|
|
18359
|
-
"freeze",
|
|
18360
|
-
"seal",
|
|
18361
|
-
"preventExtensions"
|
|
18362
|
-
]);
|
|
18363
|
-
const OBJECT_FREEZE_OR_SEAL_METHOD_NAMES = new Set(["freeze", "seal"]);
|
|
18364
|
-
const getObjectIntegrityMethodName = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
|
|
18365
|
-
const expression = stripParenExpression(node);
|
|
18366
|
-
if (!isNodeOfType(expression, "CallExpression")) return null;
|
|
18367
|
-
const callee = stripParenExpression(expression.callee);
|
|
18368
|
-
const receiver = isNodeOfType(callee, "MemberExpression") ? stripParenExpression(callee.object) : callee;
|
|
18369
|
-
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || !scopes.isGlobalReference(receiver) || !isNodeOfType(callee.property, "Identifier") || !methodNames.has(callee.property.name)) return null;
|
|
18370
|
-
return callee.property.name;
|
|
18371
|
-
};
|
|
18372
|
-
const unwrapObjectIntegrityExpression = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
|
|
18373
|
-
let expression = stripParenExpression(node);
|
|
18374
|
-
while (isNodeOfType(expression, "CallExpression")) {
|
|
18375
|
-
if (!getObjectIntegrityMethodName(expression, scopes, methodNames)) break;
|
|
18376
|
-
const wrappedExpression = expression.arguments[0];
|
|
18377
|
-
if (!wrappedExpression || isNodeOfType(wrappedExpression, "SpreadElement")) break;
|
|
18378
|
-
expression = stripParenExpression(wrappedExpression);
|
|
18379
|
-
}
|
|
18380
|
-
return expression;
|
|
18381
|
-
};
|
|
18382
|
-
//#endregion
|
|
18383
17688
|
//#region src/plugin/rules/js-performance/js-length-check-first.ts
|
|
18384
|
-
const OBJECT_CARDINALITY_PROJECTION_METHOD_NAMES = new Set(["keys", "values"]);
|
|
18385
|
-
const getGlobalObjectProjection = (expression, context) => {
|
|
18386
|
-
const callExpression = stripParenExpression(expression);
|
|
18387
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return null;
|
|
18388
|
-
const callee = stripParenExpression(callExpression.callee);
|
|
18389
|
-
if (!isNodeOfType(callee, "MemberExpression")) return null;
|
|
18390
|
-
const receiver = stripParenExpression(callee.object);
|
|
18391
|
-
if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || !context.scopes.isGlobalReference(receiver)) return null;
|
|
18392
|
-
const methodName = getStaticPropertyName(callee);
|
|
18393
|
-
if (!methodName || !OBJECT_CARDINALITY_PROJECTION_METHOD_NAMES.has(methodName)) return null;
|
|
18394
|
-
const callArguments = callExpression.arguments ?? [];
|
|
18395
|
-
if (callArguments.length !== 1 || isNodeOfType(callArguments[0], "SpreadElement")) return null;
|
|
18396
|
-
return {
|
|
18397
|
-
methodName,
|
|
18398
|
-
source: callArguments[0]
|
|
18399
|
-
};
|
|
18400
|
-
};
|
|
18401
|
-
const isPlainDataObjectLiteral = (expression) => {
|
|
18402
|
-
const objectExpression = stripParenExpression(expression);
|
|
18403
|
-
if (!isNodeOfType(objectExpression, "ObjectExpression")) return false;
|
|
18404
|
-
return (objectExpression.properties ?? []).every((property) => {
|
|
18405
|
-
if (!isNodeOfType(property, "Property") || property.kind !== "init" || property.method === true) return false;
|
|
18406
|
-
const propertyValue = stripParenExpression(property.value);
|
|
18407
|
-
return isNodeOfType(propertyValue, "Literal") || isNodeOfType(propertyValue, "TemplateLiteral") && propertyValue.expressions.length === 0;
|
|
18408
|
-
});
|
|
18409
|
-
};
|
|
18410
|
-
const getGlobalObjectFreezeArgument = (expression, context) => {
|
|
18411
|
-
const callExpression = stripParenExpression(expression);
|
|
18412
|
-
if (!isNodeOfType(callExpression, "CallExpression")) return null;
|
|
18413
|
-
if (getObjectIntegrityMethodName(callExpression, context.scopes) !== "freeze") return null;
|
|
18414
|
-
const callArguments = callExpression.arguments ?? [];
|
|
18415
|
-
if (callArguments.length !== 1 || isNodeOfType(callArguments[0], "SpreadElement")) return null;
|
|
18416
|
-
return callArguments[0];
|
|
18417
|
-
};
|
|
18418
|
-
const resolveStablePlainObjectRootId = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
18419
|
-
const source = stripParenExpression(expression);
|
|
18420
|
-
if (!isNodeOfType(source, "Identifier")) return null;
|
|
18421
|
-
const symbol = context.scopes.symbolFor(source);
|
|
18422
|
-
if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
|
|
18423
|
-
const directConstInitializer = getDirectConstInitializer(symbol);
|
|
18424
|
-
if (!directConstInitializer) return null;
|
|
18425
|
-
visitedSymbolIds.add(symbol.id);
|
|
18426
|
-
const initializer = stripParenExpression(directConstInitializer);
|
|
18427
|
-
const frozenValue = getGlobalObjectFreezeArgument(initializer, context);
|
|
18428
|
-
if (frozenValue) return isPlainDataObjectLiteral(frozenValue) ? symbol.id : null;
|
|
18429
|
-
return resolveStablePlainObjectRootId(initializer, context, visitedSymbolIds);
|
|
18430
|
-
};
|
|
18431
|
-
const areEqualCardinalityObjectProjections = (receiverArray, indexedArray, context) => {
|
|
18432
|
-
const receiverProjection = getGlobalObjectProjection(receiverArray, context);
|
|
18433
|
-
const indexedProjection = getGlobalObjectProjection(indexedArray, context);
|
|
18434
|
-
if (!receiverProjection || !indexedProjection) return false;
|
|
18435
|
-
if (receiverProjection.methodName === indexedProjection.methodName) return false;
|
|
18436
|
-
const receiverRootId = resolveStablePlainObjectRootId(receiverProjection.source, context);
|
|
18437
|
-
const indexedRootId = resolveStablePlainObjectRootId(indexedProjection.source, context);
|
|
18438
|
-
return receiverRootId !== null && receiverRootId === indexedRootId;
|
|
18439
|
-
};
|
|
18440
17689
|
const findIndexedArrayObject = (callbackBody, indexParameterName) => {
|
|
18441
17690
|
let indexedArrayObject = null;
|
|
18442
17691
|
walkAst(callbackBody, (child) => {
|
|
@@ -18651,7 +17900,6 @@ const jsLengthCheckFirst = defineRule({
|
|
|
18651
17900
|
const resolvedReceiverSource = resolveComparedArraySource(receiverArrayObject, node);
|
|
18652
17901
|
const resolvedIndexedSource = resolveComparedArraySource(indexedArrayObject, node);
|
|
18653
17902
|
if (areExpressionsStructurallyEqual(resolvedReceiverSource, resolvedIndexedSource)) return;
|
|
18654
|
-
if (areEqualCardinalityObjectProjections(receiverArrayObject, indexedArrayObject, context) || areEqualCardinalityObjectProjections(resolvedReceiverSource, resolvedIndexedSource, context)) return;
|
|
18655
17903
|
const comparedArrayPairs = [[receiverArrayObject, indexedArrayObject], [resolvedReceiverSource, resolvedIndexedSource]];
|
|
18656
17904
|
if (collectEarlierAndGuardOperands(node).some((guardOperand) => comparedArrayPairs.some(([receiverSide, indexedSide]) => isLengthComparison(guardOperand, receiverSide, indexedSide, LENGTH_ANY_COMPARISON_OPERATORS)))) return;
|
|
18657
17905
|
if (collectEarlierOrOperands(node).some((guardOperand) => comparedArrayPairs.some(([receiverSide, indexedSide]) => isLengthComparison(guardOperand, receiverSide, indexedSide, LENGTH_MISMATCH_OPERATORS)))) return;
|
|
@@ -24529,6 +23777,32 @@ const hasDirective = (programNode, directive) => {
|
|
|
24529
23777
|
return Boolean(programNode.body?.some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === directive));
|
|
24530
23778
|
};
|
|
24531
23779
|
//#endregion
|
|
23780
|
+
//#region src/plugin/utils/unwrap-object-integrity-expression.ts
|
|
23781
|
+
const OBJECT_INTEGRITY_METHOD_NAMES = new Set([
|
|
23782
|
+
"freeze",
|
|
23783
|
+
"seal",
|
|
23784
|
+
"preventExtensions"
|
|
23785
|
+
]);
|
|
23786
|
+
const OBJECT_FREEZE_OR_SEAL_METHOD_NAMES = new Set(["freeze", "seal"]);
|
|
23787
|
+
const getObjectIntegrityMethodName = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
|
|
23788
|
+
const expression = stripParenExpression(node);
|
|
23789
|
+
if (!isNodeOfType(expression, "CallExpression")) return null;
|
|
23790
|
+
const callee = stripParenExpression(expression.callee);
|
|
23791
|
+
const receiver = isNodeOfType(callee, "MemberExpression") ? stripParenExpression(callee.object) : callee;
|
|
23792
|
+
if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(receiver, "Identifier") || receiver.name !== "Object" || !scopes.isGlobalReference(receiver) || !isNodeOfType(callee.property, "Identifier") || !methodNames.has(callee.property.name)) return null;
|
|
23793
|
+
return callee.property.name;
|
|
23794
|
+
};
|
|
23795
|
+
const unwrapObjectIntegrityExpression = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
|
|
23796
|
+
let expression = stripParenExpression(node);
|
|
23797
|
+
while (isNodeOfType(expression, "CallExpression")) {
|
|
23798
|
+
if (!getObjectIntegrityMethodName(expression, scopes, methodNames)) break;
|
|
23799
|
+
const wrappedExpression = expression.arguments[0];
|
|
23800
|
+
if (!wrappedExpression || isNodeOfType(wrappedExpression, "SpreadElement")) break;
|
|
23801
|
+
expression = stripParenExpression(wrappedExpression);
|
|
23802
|
+
}
|
|
23803
|
+
return expression;
|
|
23804
|
+
};
|
|
23805
|
+
//#endregion
|
|
24532
23806
|
//#region src/plugin/rules/nextjs/nextjs-async-client-component.ts
|
|
24533
23807
|
const nextjsAsyncClientComponent = defineRule({
|
|
24534
23808
|
id: "nextjs-async-client-component",
|
|
@@ -26868,7 +26142,7 @@ const isIndependentWriterIdentifier = (componentFunction, identifier, includeDef
|
|
|
26868
26142
|
};
|
|
26869
26143
|
const hasUserInputSetterWriter = (setterRef, effectNode, includeDeferredWriters = false) => {
|
|
26870
26144
|
if (!setterRef.resolved) return false;
|
|
26871
|
-
const componentFunction = findEnclosingFunction
|
|
26145
|
+
const componentFunction = findEnclosingFunction(effectNode);
|
|
26872
26146
|
if (!componentFunction) return false;
|
|
26873
26147
|
for (const reference of setterRef.resolved.references) {
|
|
26874
26148
|
if (reference.init) continue;
|
|
@@ -29641,6 +28915,138 @@ const noBarrelImport = defineRule({
|
|
|
29641
28915
|
}
|
|
29642
28916
|
});
|
|
29643
28917
|
//#endregion
|
|
28918
|
+
//#region src/plugin/utils/has-static-property-write-before.ts
|
|
28919
|
+
const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
|
|
28920
|
+
const CONDITIONAL_EXECUTION_NODE_TYPES = new Set([
|
|
28921
|
+
"CatchClause",
|
|
28922
|
+
"ConditionalExpression",
|
|
28923
|
+
"DoWhileStatement",
|
|
28924
|
+
"ForInStatement",
|
|
28925
|
+
"ForOfStatement",
|
|
28926
|
+
"ForStatement",
|
|
28927
|
+
"IfStatement",
|
|
28928
|
+
"LogicalExpression",
|
|
28929
|
+
"SwitchCase",
|
|
28930
|
+
"SwitchStatement",
|
|
28931
|
+
"TryStatement",
|
|
28932
|
+
"WhileStatement"
|
|
28933
|
+
]);
|
|
28934
|
+
const getResolvedStaticPropertyName = (memberExpression, scopes) => {
|
|
28935
|
+
if (!isNodeOfType(memberExpression, "MemberExpression")) return null;
|
|
28936
|
+
const directPropertyName = getStaticPropertyName(memberExpression);
|
|
28937
|
+
if (directPropertyName || !memberExpression.computed) return directPropertyName;
|
|
28938
|
+
const property = stripParenExpression(memberExpression.property);
|
|
28939
|
+
if (!isNodeOfType(property, "Identifier")) return null;
|
|
28940
|
+
const propertySymbol = resolveConstIdentifierAlias(property, scopes);
|
|
28941
|
+
const initializer = propertySymbol?.initializer ? stripParenExpression(propertySymbol.initializer) : null;
|
|
28942
|
+
return initializer && isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
|
|
28943
|
+
};
|
|
28944
|
+
const collectScopeSymbols = (scope, symbols) => {
|
|
28945
|
+
symbols.push(...scope.symbols);
|
|
28946
|
+
for (const childScope of scope.children) collectScopeSymbols(childScope, symbols);
|
|
28947
|
+
};
|
|
28948
|
+
const getEquivalentSymbols = (identifier, scopes) => {
|
|
28949
|
+
const rootSymbol = resolveConstIdentifierAlias(identifier, scopes);
|
|
28950
|
+
if (!rootSymbol) return [];
|
|
28951
|
+
let symbolsByRootId = equivalentSymbolsByAnalysis.get(scopes);
|
|
28952
|
+
if (!symbolsByRootId) {
|
|
28953
|
+
symbolsByRootId = /* @__PURE__ */ new Map();
|
|
28954
|
+
equivalentSymbolsByAnalysis.set(scopes, symbolsByRootId);
|
|
28955
|
+
}
|
|
28956
|
+
const cachedSymbols = symbolsByRootId.get(rootSymbol.id);
|
|
28957
|
+
if (cachedSymbols) return cachedSymbols;
|
|
28958
|
+
const allSymbols = [];
|
|
28959
|
+
collectScopeSymbols(scopes.rootScope, allSymbols);
|
|
28960
|
+
const equivalentSymbols = allSymbols.filter((symbol) => resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes)?.id === rootSymbol.id);
|
|
28961
|
+
symbolsByRootId.set(rootSymbol.id, equivalentSymbols);
|
|
28962
|
+
return equivalentSymbols;
|
|
28963
|
+
};
|
|
28964
|
+
const findExecutionBoundary = (node) => {
|
|
28965
|
+
let current = node;
|
|
28966
|
+
while (current) {
|
|
28967
|
+
if (isFunctionLike$2(current) || isNodeOfType(current, "Program")) return current;
|
|
28968
|
+
current = current.parent ?? null;
|
|
28969
|
+
}
|
|
28970
|
+
return null;
|
|
28971
|
+
};
|
|
28972
|
+
const isOnUnconditionalPath = (node, boundary) => {
|
|
28973
|
+
let current = node.parent ?? null;
|
|
28974
|
+
while (current && current !== boundary) {
|
|
28975
|
+
if (CONDITIONAL_EXECUTION_NODE_TYPES.has(current.type)) return false;
|
|
28976
|
+
current = current.parent ?? null;
|
|
28977
|
+
}
|
|
28978
|
+
return current === boundary;
|
|
28979
|
+
};
|
|
28980
|
+
const findFunctionBindingIdentifier = (functionNode) => {
|
|
28981
|
+
if (isNodeOfType(functionNode, "FunctionDeclaration")) return functionNode.id ?? null;
|
|
28982
|
+
const parent = functionNode.parent;
|
|
28983
|
+
if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.init === functionNode && isNodeOfType(parent.id, "Identifier")) return parent.id;
|
|
28984
|
+
return null;
|
|
28985
|
+
};
|
|
28986
|
+
const findDirectCall = (identifier) => {
|
|
28987
|
+
let callee = identifier;
|
|
28988
|
+
let parent = callee.parent;
|
|
28989
|
+
while (parent && stripParenExpression(parent) === identifier) {
|
|
28990
|
+
callee = parent;
|
|
28991
|
+
parent = callee.parent;
|
|
28992
|
+
}
|
|
28993
|
+
return parent && isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
|
|
28994
|
+
};
|
|
28995
|
+
const isFunctionSynchronouslyInvokedBefore = (functionNode, referenceNode, scopes, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
|
|
28996
|
+
if (visitedFunctionNodes.has(functionNode) || !isFunctionLike$2(functionNode) || functionNode.generator) return false;
|
|
28997
|
+
visitedFunctionNodes.add(functionNode);
|
|
28998
|
+
const referenceBoundary = findExecutionBoundary(referenceNode);
|
|
28999
|
+
if (!referenceBoundary) return false;
|
|
29000
|
+
const invocationCalls = [];
|
|
29001
|
+
const bindingIdentifier = findFunctionBindingIdentifier(functionNode);
|
|
29002
|
+
if (bindingIdentifier) for (const symbol of getEquivalentSymbols(bindingIdentifier, scopes)) for (const reference of symbol.references) {
|
|
29003
|
+
const call = findDirectCall(reference.identifier);
|
|
29004
|
+
if (call) invocationCalls.push(call);
|
|
29005
|
+
}
|
|
29006
|
+
else {
|
|
29007
|
+
const call = findDirectCall(functionNode);
|
|
29008
|
+
if (call) invocationCalls.push(call);
|
|
29009
|
+
}
|
|
29010
|
+
return invocationCalls.some((call) => {
|
|
29011
|
+
if (call.range[0] >= referenceNode.range[0]) return false;
|
|
29012
|
+
const callBoundary = findExecutionBoundary(call);
|
|
29013
|
+
if (!callBoundary) return false;
|
|
29014
|
+
if (callBoundary === referenceBoundary) return true;
|
|
29015
|
+
if (!isFunctionLike$2(callBoundary)) return false;
|
|
29016
|
+
return isFunctionSynchronouslyInvokedBefore(callBoundary, referenceNode, scopes, new Set(visitedFunctionNodes));
|
|
29017
|
+
});
|
|
29018
|
+
};
|
|
29019
|
+
const isMemberWriteTarget = (memberExpression) => {
|
|
29020
|
+
const parent = memberExpression.parent;
|
|
29021
|
+
if (!parent) return false;
|
|
29022
|
+
if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === memberExpression;
|
|
29023
|
+
if (isNodeOfType(parent, "UpdateExpression")) return parent.argument === memberExpression;
|
|
29024
|
+
return isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === memberExpression;
|
|
29025
|
+
};
|
|
29026
|
+
const symbolHasStaticPropertyWriteBefore = (symbol, propertyName, referenceNode, scopes) => symbol.references.some((reference) => {
|
|
29027
|
+
let parent = reference.identifier.parent;
|
|
29028
|
+
while (parent && stripParenExpression(parent) === reference.identifier) parent = parent.parent;
|
|
29029
|
+
if (!parent || !isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== reference.identifier || getResolvedStaticPropertyName(parent, scopes) !== propertyName || !isMemberWriteTarget(parent)) return false;
|
|
29030
|
+
const writeBoundary = findExecutionBoundary(parent);
|
|
29031
|
+
const referenceBoundary = findExecutionBoundary(referenceNode);
|
|
29032
|
+
if (!writeBoundary || !referenceBoundary || !isOnUnconditionalPath(parent, writeBoundary)) return false;
|
|
29033
|
+
if (writeBoundary === referenceBoundary) return parent.range[0] < referenceNode.range[0];
|
|
29034
|
+
return isFunctionLike$2(writeBoundary) && isFunctionSynchronouslyInvokedBefore(writeBoundary, referenceNode, scopes);
|
|
29035
|
+
});
|
|
29036
|
+
const hasStaticPropertyWriteBefore = (identifier, propertyName, referenceNode, scopes) => {
|
|
29037
|
+
if (!isNodeOfType(identifier, "Identifier")) return false;
|
|
29038
|
+
return getEquivalentSymbols(identifier, scopes).some((symbol) => symbolHasStaticPropertyWriteBefore(symbol, propertyName, referenceNode, scopes));
|
|
29039
|
+
};
|
|
29040
|
+
//#endregion
|
|
29041
|
+
//#region src/plugin/utils/has-symbol-write-before.ts
|
|
29042
|
+
const hasSymbolWriteBefore = (symbol, referenceNode, scopes) => symbol.references.some((reference) => {
|
|
29043
|
+
if (reference.flag === "read") return false;
|
|
29044
|
+
const writeFunction = findEnclosingFunction(reference.identifier);
|
|
29045
|
+
if (writeFunction === findEnclosingFunction(referenceNode)) return reference.identifier.range[0] < referenceNode.range[0];
|
|
29046
|
+
if (!writeFunction) return true;
|
|
29047
|
+
return isFunctionSynchronouslyInvokedBefore(writeFunction, referenceNode, scopes);
|
|
29048
|
+
});
|
|
29049
|
+
//#endregion
|
|
29644
29050
|
//#region src/plugin/utils/has-stable-call-target.ts
|
|
29645
29051
|
const hasStableCallTarget = (callExpression, scopes) => {
|
|
29646
29052
|
if (!isNodeOfType(callExpression, "CallExpression")) return false;
|
|
@@ -30109,8 +29515,8 @@ const isUseMemoCallbackArgument = (functionNode) => {
|
|
|
30109
29515
|
return isReactFunctionCall(parent, "useMemo");
|
|
30110
29516
|
};
|
|
30111
29517
|
const findEnclosingRenderFunction = (node) => {
|
|
30112
|
-
let enclosingFunction = findEnclosingFunction
|
|
30113
|
-
while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction
|
|
29518
|
+
let enclosingFunction = findEnclosingFunction(node);
|
|
29519
|
+
while (enclosingFunction && isUseMemoCallbackArgument(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
|
|
30114
29520
|
return enclosingFunction;
|
|
30115
29521
|
};
|
|
30116
29522
|
const noCreateRefInFunctionComponent = defineRule({
|
|
@@ -31161,7 +30567,7 @@ const getEnclosingEffectHookCallback = (node, componentFunction) => {
|
|
|
31161
30567
|
const isEffectDrivenResync = (useStateCall) => {
|
|
31162
30568
|
const setterName = getStateSetterName(useStateCall);
|
|
31163
30569
|
if (!setterName) return false;
|
|
31164
|
-
const componentFunction = findEnclosingFunction
|
|
30570
|
+
const componentFunction = findEnclosingFunction(useStateCall);
|
|
31165
30571
|
if (!componentFunction) return false;
|
|
31166
30572
|
let isExempt = false;
|
|
31167
30573
|
walkAst(componentFunction, (child) => {
|
|
@@ -31265,7 +30671,7 @@ const isDraftReseedOrRenderAdjusted = (useStateCall, isPropName) => {
|
|
|
31265
30671
|
const setterName = getStateSetterName(useStateCall);
|
|
31266
30672
|
if (!setterName) return false;
|
|
31267
30673
|
const stateValueName = getStateValueName(useStateCall);
|
|
31268
|
-
const componentFunction = findEnclosingFunction
|
|
30674
|
+
const componentFunction = findEnclosingFunction(useStateCall);
|
|
31269
30675
|
if (!componentFunction) return false;
|
|
31270
30676
|
let isExempt = false;
|
|
31271
30677
|
walkAst(componentFunction, (child) => {
|
|
@@ -31311,7 +30717,7 @@ const noDerivedUseState = defineRule({
|
|
|
31311
30717
|
if (isDraftReseedOrRenderAdjusted(node, propStackTracker.isPropName)) return;
|
|
31312
30718
|
if (isEffectDrivenResync(node)) return;
|
|
31313
30719
|
if (isNextjsDataFetchingPage(node)) return;
|
|
31314
|
-
const componentFunction = findEnclosingFunction
|
|
30720
|
+
const componentFunction = findEnclosingFunction(node);
|
|
31315
30721
|
if (componentFunction) {
|
|
31316
30722
|
if (isDefaultedDestructuredProp(componentFunction, propName)) return;
|
|
31317
30723
|
if (isDraftCommittedToParent(componentFunction, getStateValueName(node), propStackTracker.isPropName)) return;
|
|
@@ -33149,7 +32555,7 @@ const isFunctionUsedOutsideHandlers = (analysis, functionNode, componentFunction
|
|
|
33149
32555
|
if (isInsideInlineEventHandler(identifier, componentFunction)) return false;
|
|
33150
32556
|
const parent = identifier.parent;
|
|
33151
32557
|
if (parent && isNodeOfType(parent, "CallExpression") && parent.callee === identifier) {
|
|
33152
|
-
if (findEnclosingFunction
|
|
32558
|
+
if (findEnclosingFunction(parent) === functionNode) return false;
|
|
33153
32559
|
if (isInsideProvenEventHandler(analysis, parent, componentFunction, false)) return false;
|
|
33154
32560
|
}
|
|
33155
32561
|
return true;
|
|
@@ -33195,7 +32601,7 @@ const isStateWrittenOnlyFromEventHandlers = (analysis, stateReference) => {
|
|
|
33195
32601
|
if (!setterVariable) return false;
|
|
33196
32602
|
const stateDeclarator = getUseStateDecl(analysis, stateReference);
|
|
33197
32603
|
if (!stateDeclarator) return false;
|
|
33198
|
-
const componentFunction = findEnclosingFunction
|
|
32604
|
+
const componentFunction = findEnclosingFunction(stateDeclarator);
|
|
33199
32605
|
if (!componentFunction) return false;
|
|
33200
32606
|
let hasWriter = false;
|
|
33201
32607
|
for (const reference of setterVariable.references) {
|
|
@@ -34257,8 +33663,8 @@ const isAwaitedInFunction = (request, functionNode) => {
|
|
|
34257
33663
|
return false;
|
|
34258
33664
|
};
|
|
34259
33665
|
const isCompletionSinkForRequest = (completionSink, request) => {
|
|
34260
|
-
const requestFunction = findEnclosingFunction
|
|
34261
|
-
const completionSinkFunction = findEnclosingFunction
|
|
33666
|
+
const requestFunction = findEnclosingFunction(request);
|
|
33667
|
+
const completionSinkFunction = findEnclosingFunction(completionSink);
|
|
34262
33668
|
if (!requestFunction || !completionSinkFunction) return false;
|
|
34263
33669
|
if (requestFunction === completionSinkFunction) {
|
|
34264
33670
|
if (!isAwaitedInFunction(request, requestFunction)) return false;
|
|
@@ -34545,6 +33951,13 @@ const isPublishedLibraryPackage = (filename) => {
|
|
|
34545
33951
|
return manifest.private !== true && typeof manifest.peerDependencies === "object" && manifest.peerDependencies !== null && "react" in manifest.peerDependencies;
|
|
34546
33952
|
};
|
|
34547
33953
|
//#endregion
|
|
33954
|
+
//#region src/plugin/utils/is-type-only-import.ts
|
|
33955
|
+
const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
|
|
33956
|
+
if (!specifiers || specifiers.length === 0) return false;
|
|
33957
|
+
return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
|
|
33958
|
+
};
|
|
33959
|
+
const isTypeOnlyImport = (node) => node.importKind === "type" || isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind");
|
|
33960
|
+
//#endregion
|
|
34548
33961
|
//#region src/plugin/rules/bundle-size/no-full-lodash-import.ts
|
|
34549
33962
|
const isLibraryDevPage = (filename) => {
|
|
34550
33963
|
if (!filename) return false;
|
|
@@ -35108,7 +34521,7 @@ const isInRenderedOutput = (node, componentOrHookNode, scopes) => {
|
|
|
35108
34521
|
return attribute ? !isEventHandlerAttribute(attribute) : true;
|
|
35109
34522
|
}
|
|
35110
34523
|
if (isNodeOfType(parentNode, "ReturnStatement")) {
|
|
35111
|
-
if (findEnclosingFunction
|
|
34524
|
+
if (findEnclosingFunction(parentNode) === componentOrHookNode) return true;
|
|
35112
34525
|
}
|
|
35113
34526
|
if (parentNode === componentOrHookNode) return isFunctionLike$2(componentOrHookNode) && !isNodeOfType(componentOrHookNode.body, "BlockStatement") && componentOrHookNode.body === currentNode;
|
|
35114
34527
|
if (isFunctionLike$2(parentNode) && !executesDuringRender(parentNode, scopes)) return false;
|
|
@@ -35231,7 +34644,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
|
|
|
35231
34644
|
if (consequentValues.length === 0 || alternateValues.length === 0) return;
|
|
35232
34645
|
const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
|
|
35233
34646
|
if (!componentOrHookNode) return;
|
|
35234
|
-
const enclosingFunction = findEnclosingFunction
|
|
34647
|
+
const enclosingFunction = findEnclosingFunction(node);
|
|
35235
34648
|
if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
|
|
35236
34649
|
for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
|
|
35237
34650
|
if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
|
|
@@ -35679,10 +35092,10 @@ const isInsideInstanceField = (node) => {
|
|
|
35679
35092
|
return false;
|
|
35680
35093
|
};
|
|
35681
35094
|
const isOneShotModuleInitialization = (node, context) => {
|
|
35682
|
-
let functionNode = findEnclosingFunction
|
|
35095
|
+
let functionNode = findEnclosingFunction(node);
|
|
35683
35096
|
while (functionNode) {
|
|
35684
35097
|
if (!executesDuringRender(functionNode, context.scopes)) return false;
|
|
35685
|
-
functionNode = findEnclosingFunction
|
|
35098
|
+
functionNode = findEnclosingFunction(functionNode);
|
|
35686
35099
|
}
|
|
35687
35100
|
return !isInsideInstanceField(node);
|
|
35688
35101
|
};
|
|
@@ -36666,10 +36079,10 @@ const getMethodCalls = (functionNode, scopes) => {
|
|
|
36666
36079
|
const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, scopes, visitedSymbolIds, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
|
|
36667
36080
|
if (visitedFunctionNodes.has(functionNode)) return false;
|
|
36668
36081
|
visitedFunctionNodes.add(functionNode);
|
|
36669
|
-
const usageFunction = findEnclosingFunction
|
|
36082
|
+
const usageFunction = findEnclosingFunction(usageNode);
|
|
36670
36083
|
const immediateCall = getDirectCallForExpression(functionNode);
|
|
36671
36084
|
if (immediateCall) {
|
|
36672
|
-
const immediateCallFunction = findEnclosingFunction
|
|
36085
|
+
const immediateCallFunction = findEnclosingFunction(immediateCall);
|
|
36673
36086
|
if (immediateCallFunction === usageFunction) {
|
|
36674
36087
|
const immediateCallStart = getRangeStart(immediateCall);
|
|
36675
36088
|
return immediateCallStart === null || immediateCallStart < usageBoundary;
|
|
@@ -36678,7 +36091,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
|
|
|
36678
36091
|
return isFunctionInvokedBeforeUsage(immediateCallFunction, usageNode, usageBoundary, scopes, visitedSymbolIds, new Set(visitedFunctionNodes));
|
|
36679
36092
|
}
|
|
36680
36093
|
for (const methodCall of getMethodCalls(functionNode, scopes)) {
|
|
36681
|
-
const methodCallFunction = findEnclosingFunction
|
|
36094
|
+
const methodCallFunction = findEnclosingFunction(methodCall);
|
|
36682
36095
|
if (methodCallFunction === usageFunction) {
|
|
36683
36096
|
const methodCallStart = getRangeStart(methodCall);
|
|
36684
36097
|
if (methodCallStart === null || methodCallStart < usageBoundary) return true;
|
|
@@ -36701,7 +36114,7 @@ const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, sc
|
|
|
36701
36114
|
if (scopes.symbolFor(child)?.declarationNode !== symbol.declarationNode) return;
|
|
36702
36115
|
const call = getDirectCallForExpression(child);
|
|
36703
36116
|
if (!call) return false;
|
|
36704
|
-
const callFunction = findEnclosingFunction
|
|
36117
|
+
const callFunction = findEnclosingFunction(call);
|
|
36705
36118
|
if (callFunction === usageFunction) {
|
|
36706
36119
|
const callStart = getRangeStart(call);
|
|
36707
36120
|
wasInvokedBeforeUsage = callStart === null || callStart < usageBoundary;
|
|
@@ -36732,14 +36145,14 @@ const wasMutatedBeforeUsage = (symbol, usageNode, readNode, scopes, visitedMutat
|
|
|
36732
36145
|
visitedMutationSymbolIds.add(symbol.id);
|
|
36733
36146
|
const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, readNode) : inheritedUsageBoundary;
|
|
36734
36147
|
if (typeof usageBoundary !== "number") return true;
|
|
36735
|
-
const usageFunction = findEnclosingFunction
|
|
36148
|
+
const usageFunction = findEnclosingFunction(usageNode);
|
|
36736
36149
|
return symbol.references.some((reference) => {
|
|
36737
36150
|
const referenceStart = getRangeStart(reference.identifier);
|
|
36738
36151
|
const simpleAlias = getSimpleAlias(reference.identifier, scopes);
|
|
36739
36152
|
if (simpleAlias) return wasMutatedBeforeUsage(simpleAlias.symbol, usageNode, readNodesBySymbolId.get(simpleAlias.symbol.id) ?? simpleAlias.readNode, scopes, new Set(visitedMutationSymbolIds), usageBoundary, readNodesBySymbolId);
|
|
36740
36153
|
if (!isPotentialMutationReference(reference.identifier, readNode)) return false;
|
|
36741
36154
|
if (referenceStart === null) return true;
|
|
36742
|
-
const mutationFunction = findEnclosingFunction
|
|
36155
|
+
const mutationFunction = findEnclosingFunction(reference.identifier);
|
|
36743
36156
|
if (mutationFunction === usageFunction) return referenceStart < usageBoundary;
|
|
36744
36157
|
if (!mutationFunction) return usageFunction !== null;
|
|
36745
36158
|
if (usageFunction && isAstDescendant(usageFunction, mutationFunction)) return true;
|
|
@@ -37321,6 +36734,13 @@ const isReactNamespaceExpression = (node, scopes) => {
|
|
|
37321
36734
|
return isReactNamespaceImport(node, scopes);
|
|
37322
36735
|
};
|
|
37323
36736
|
const isReactHocMemberReference = (node, scopes) => Boolean(isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.property, "Identifier") && REACT_HOC_NAMES.has(node.property.name) && isReactNamespaceExpression(node.object, scopes));
|
|
36737
|
+
const getDestructuredPropertyName$1 = (symbol) => {
|
|
36738
|
+
const property = symbol.bindingIdentifier.parent;
|
|
36739
|
+
if (!property || !isNodeOfType(property, "Property") || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return null;
|
|
36740
|
+
if (isNodeOfType(property.key, "Identifier") && !property.computed) return property.key.name;
|
|
36741
|
+
if (isNodeOfType(property.key, "Literal") && typeof property.key.value === "string") return property.key.value;
|
|
36742
|
+
return null;
|
|
36743
|
+
};
|
|
37324
36744
|
const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
|
|
37325
36745
|
if (visitedSymbolIds.has(symbol.id)) return false;
|
|
37326
36746
|
visitedSymbolIds.add(symbol.id);
|
|
@@ -37330,7 +36750,7 @@ const symbolMapsToHoc = (symbol, scopes, visitedSymbolIds) => {
|
|
|
37330
36750
|
}
|
|
37331
36751
|
const init = symbol.initializer;
|
|
37332
36752
|
if (!init) return false;
|
|
37333
|
-
const destructuredPropertyName =
|
|
36753
|
+
const destructuredPropertyName = getDestructuredPropertyName$1(symbol);
|
|
37334
36754
|
if (destructuredPropertyName && REACT_HOC_NAMES.has(destructuredPropertyName) && isReactNamespaceExpression(init, scopes)) return true;
|
|
37335
36755
|
if (isReactHocMemberReference(init, scopes)) return true;
|
|
37336
36756
|
if (isNodeOfType(init, "Identifier")) {
|
|
@@ -39084,6 +38504,12 @@ const getDeclarationKind = (declarator) => {
|
|
|
39084
38504
|
return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
|
|
39085
38505
|
};
|
|
39086
38506
|
const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
|
|
38507
|
+
const getDestructuredPropertyName = (bindingIdentifier) => {
|
|
38508
|
+
const property = bindingIdentifier.parent;
|
|
38509
|
+
if (!property || !isNodeOfType(property, "Property")) return null;
|
|
38510
|
+
if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
|
|
38511
|
+
return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
|
|
38512
|
+
};
|
|
39087
38513
|
const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
|
|
39088
38514
|
const unwrappedExpression = stripParenExpression(expression);
|
|
39089
38515
|
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
@@ -39093,7 +38519,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
|
|
|
39093
38519
|
if (isProp(analysis, callbackReference)) {
|
|
39094
38520
|
if (isWholePropsObjectReference(analysis, callbackReference)) return null;
|
|
39095
38521
|
const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
|
|
39096
|
-
return (bindingIdentifier &&
|
|
38522
|
+
return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
|
|
39097
38523
|
}
|
|
39098
38524
|
if (hasMutableBindingWrite(callbackReference)) return null;
|
|
39099
38525
|
const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
|
|
@@ -39103,7 +38529,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
|
|
|
39103
38529
|
visitedVariables.add(callbackVariable);
|
|
39104
38530
|
if (isNodeOfType(declarator.id, "ObjectPattern")) {
|
|
39105
38531
|
const bindingIdentifier = callbackVariable.defs[0]?.name;
|
|
39106
|
-
const propertyName = bindingIdentifier ?
|
|
38532
|
+
const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
|
|
39107
38533
|
const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
|
|
39108
38534
|
return propertyName && propsReference ? propertyName : null;
|
|
39109
38535
|
}
|
|
@@ -39206,7 +38632,7 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
|
|
|
39206
38632
|
const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
|
|
39207
38633
|
if (!bindingProvenance) return null;
|
|
39208
38634
|
const { refCall, variables } = bindingProvenance;
|
|
39209
|
-
const componentFunction = findEnclosingFunction
|
|
38635
|
+
const componentFunction = findEnclosingFunction(effectCall);
|
|
39210
38636
|
if (!componentFunction || !isFunctionLike$2(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
|
|
39211
38637
|
if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
|
|
39212
38638
|
const callbackPropNames = /* @__PURE__ */ new Set();
|
|
@@ -40194,7 +39620,7 @@ const isModuleScopedBinding = (identifier) => {
|
|
|
40194
39620
|
const binding = findVariableInitializer(identifier, identifier.name);
|
|
40195
39621
|
if (!binding) return false;
|
|
40196
39622
|
if (isNodeOfType(binding.scopeOwner, "Program")) return true;
|
|
40197
|
-
if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction
|
|
39623
|
+
if (isNodeOfType(binding.scopeOwner, "BlockStatement")) return findEnclosingFunction(binding.scopeOwner) === null;
|
|
40198
39624
|
return false;
|
|
40199
39625
|
};
|
|
40200
39626
|
const looksLikeFreshUpdateExpression = (expression) => {
|
|
@@ -42111,9 +41537,9 @@ const isReturnedFromAnyEffectInScope = (functionNode, ownerScope) => {
|
|
|
42111
41537
|
return isReturnedFromEffect;
|
|
42112
41538
|
};
|
|
42113
41539
|
const isInsideEffectCleanupReturn = (node, ownerScope) => {
|
|
42114
|
-
let functionNode = findEnclosingFunction
|
|
41540
|
+
let functionNode = findEnclosingFunction(node);
|
|
42115
41541
|
while (functionNode) {
|
|
42116
|
-
const outerFunction = findEnclosingFunction
|
|
41542
|
+
const outerFunction = findEnclosingFunction(functionNode);
|
|
42117
41543
|
if (outerFunction && isEffectCallbackFunction(outerFunction) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
|
|
42118
41544
|
if (isReturnedFromAnyEffectInScope(functionNode, ownerScope)) return true;
|
|
42119
41545
|
functionNode = outerFunction;
|
|
@@ -42121,7 +41547,7 @@ const isInsideEffectCleanupReturn = (node, ownerScope) => {
|
|
|
42121
41547
|
return false;
|
|
42122
41548
|
};
|
|
42123
41549
|
const hasRefCurrentReassignmentAfterClear = (clearCall, refName) => {
|
|
42124
|
-
const enclosingFunction = findEnclosingFunction
|
|
41550
|
+
const enclosingFunction = findEnclosingFunction(clearCall);
|
|
42125
41551
|
if (!isFunctionLike$2(enclosingFunction)) return false;
|
|
42126
41552
|
if (!isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
|
|
42127
41553
|
const clearStart = getRangeStart(clearCall);
|
|
@@ -42951,7 +42377,7 @@ const isInsideAvailabilityGuard = (node, browserGlobalName, context) => {
|
|
|
42951
42377
|
return false;
|
|
42952
42378
|
};
|
|
42953
42379
|
const isAfterAvailabilityEarlyExit = (node, componentOrHookNode, browserGlobalName, context) => {
|
|
42954
|
-
const enclosingFunction = findEnclosingFunction
|
|
42380
|
+
const enclosingFunction = findEnclosingFunction(node);
|
|
42955
42381
|
if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, context.scopes) || !isFunctionLike$2(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
|
|
42956
42382
|
let currentNode = node;
|
|
42957
42383
|
while (currentNode !== enclosingFunction) {
|
|
@@ -44218,11 +43644,7 @@ const resolveSettings$8 = (settings) => {
|
|
|
44218
43644
|
propNamePattern: ruleSettings.propNamePattern ?? "render*"
|
|
44219
43645
|
};
|
|
44220
43646
|
};
|
|
44221
|
-
const
|
|
44222
|
-
allowGlobalReactNamespace: true,
|
|
44223
|
-
resolveNamedAliases: true
|
|
44224
|
-
});
|
|
44225
|
-
const expressionContainsJsxOrCreateElement = (root, scopes) => {
|
|
43647
|
+
const expressionContainsJsxOrCreateElement = (root) => {
|
|
44226
43648
|
let found = false;
|
|
44227
43649
|
walkAst(root, (node) => {
|
|
44228
43650
|
if (found) return false;
|
|
@@ -44231,17 +43653,17 @@ const expressionContainsJsxOrCreateElement = (root, scopes) => {
|
|
|
44231
43653
|
found = true;
|
|
44232
43654
|
return false;
|
|
44233
43655
|
}
|
|
44234
|
-
if (isNodeOfType(node, "CallExpression") &&
|
|
43656
|
+
if (isNodeOfType(node, "CallExpression") && isCreateElementCall(node)) {
|
|
44235
43657
|
found = true;
|
|
44236
43658
|
return false;
|
|
44237
43659
|
}
|
|
44238
43660
|
});
|
|
44239
43661
|
return found;
|
|
44240
43662
|
};
|
|
44241
|
-
const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode
|
|
44242
|
-
const isReactClassComponent = (classNode
|
|
43663
|
+
const functionContainsJsxOrCreateElement = (functionNode, scopes, controlFlow) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement, controlFlow);
|
|
43664
|
+
const isReactClassComponent = (classNode) => {
|
|
44243
43665
|
if (isEs6Component(classNode)) return true;
|
|
44244
|
-
return expressionContainsJsxOrCreateElement(classNode
|
|
43666
|
+
return expressionContainsJsxOrCreateElement(classNode);
|
|
44245
43667
|
};
|
|
44246
43668
|
const findEnclosingComponent = (node, scopes, controlFlow) => {
|
|
44247
43669
|
let walker = node.parent;
|
|
@@ -44258,7 +43680,7 @@ const findEnclosingComponent = (node, scopes, controlFlow) => {
|
|
|
44258
43680
|
};
|
|
44259
43681
|
}
|
|
44260
43682
|
if (isNodeOfType(walker, "ClassDeclaration") || isNodeOfType(walker, "ClassExpression")) {
|
|
44261
|
-
if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker
|
|
43683
|
+
if (walker.id && isReactComponentName(walker.id.name) && isReactClassComponent(walker)) return {
|
|
44262
43684
|
component: walker,
|
|
44263
43685
|
name: walker.id.name
|
|
44264
43686
|
};
|
|
@@ -44347,12 +43769,12 @@ const isObjectCallbackCandidate = (node) => {
|
|
|
44347
43769
|
}
|
|
44348
43770
|
return false;
|
|
44349
43771
|
};
|
|
44350
|
-
const hocCallContainsComponent = (call
|
|
43772
|
+
const hocCallContainsComponent = (call) => {
|
|
44351
43773
|
const firstArgument = call.arguments[0];
|
|
44352
43774
|
if (!firstArgument) return false;
|
|
44353
|
-
if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument
|
|
44354
|
-
if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument
|
|
44355
|
-
return expressionContainsJsxOrCreateElement(firstArgument
|
|
43775
|
+
if (isNodeOfType(firstArgument, "FunctionExpression") || isNodeOfType(firstArgument, "ArrowFunctionExpression") || isNodeOfType(firstArgument, "ClassExpression")) return expressionContainsJsxOrCreateElement(firstArgument);
|
|
43776
|
+
if (isNodeOfType(firstArgument, "CallExpression") && isHocCallee$1(firstArgument)) return hocCallContainsComponent(firstArgument);
|
|
43777
|
+
return expressionContainsJsxOrCreateElement(firstArgument);
|
|
44356
43778
|
};
|
|
44357
43779
|
const isFirstArgumentOfHocCall = (node) => {
|
|
44358
43780
|
const parent = node.parent;
|
|
@@ -44385,35 +43807,19 @@ const isReturnOfMapCallback = (node) => {
|
|
|
44385
43807
|
}
|
|
44386
43808
|
return false;
|
|
44387
43809
|
};
|
|
43810
|
+
const isCloneElementCall = (node) => {
|
|
43811
|
+
if (!isNodeOfType(node, "CallExpression")) return false;
|
|
43812
|
+
const callee = node.callee;
|
|
43813
|
+
if (isNodeOfType(callee, "Identifier")) return callee.name === "cloneElement";
|
|
43814
|
+
return isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "cloneElement";
|
|
43815
|
+
};
|
|
44388
43816
|
const TS_VALUE_PASSTHROUGH_TYPES = new Set([
|
|
44389
43817
|
"TSAsExpression",
|
|
44390
43818
|
"TSNonNullExpression",
|
|
44391
43819
|
"TSSatisfiesExpression",
|
|
44392
43820
|
"TSTypeAssertion"
|
|
44393
43821
|
]);
|
|
44394
|
-
const
|
|
44395
|
-
"as",
|
|
44396
|
-
"body",
|
|
44397
|
-
"calendarcontainer",
|
|
44398
|
-
"component",
|
|
44399
|
-
"fallback",
|
|
44400
|
-
"tooltip"
|
|
44401
|
-
]);
|
|
44402
|
-
const isElementTypeJsxAttribute = (node) => {
|
|
44403
|
-
if (!isNodeOfType(node, "JSXAttribute")) return false;
|
|
44404
|
-
if (!isNodeOfType(node.name, "JSXIdentifier")) return false;
|
|
44405
|
-
const attributeName = node.name.name;
|
|
44406
|
-
return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
|
|
44407
|
-
};
|
|
44408
|
-
const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
|
|
44409
|
-
allowGlobalReactNamespace: true,
|
|
44410
|
-
resolveNamedAliases: true
|
|
44411
|
-
});
|
|
44412
|
-
const isReactLazyCall = (call, scopes) => isReactApiCall(call, "lazy", scopes, {
|
|
44413
|
-
allowGlobalReactNamespace: true,
|
|
44414
|
-
resolveNamedAliases: true
|
|
44415
|
-
});
|
|
44416
|
-
const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
|
|
43822
|
+
const isRenderFlowingReadReference = (identifier) => {
|
|
44417
43823
|
let valueNode = identifier;
|
|
44418
43824
|
let parent = valueNode.parent;
|
|
44419
43825
|
while (parent) {
|
|
@@ -44423,26 +43829,20 @@ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @_
|
|
|
44423
43829
|
continue;
|
|
44424
43830
|
}
|
|
44425
43831
|
switch (parent.type) {
|
|
44426
|
-
case "
|
|
44427
|
-
case "
|
|
44428
|
-
case "
|
|
44429
|
-
case "ArrowFunctionExpression": return false;
|
|
43832
|
+
case "JSXExpressionContainer":
|
|
43833
|
+
case "ReturnStatement": return true;
|
|
43834
|
+
case "ArrowFunctionExpression": return parent.body === valueNode;
|
|
44430
43835
|
case "CallExpression":
|
|
44431
43836
|
if (parent.callee === valueNode) return false;
|
|
44432
|
-
if (
|
|
44433
|
-
if (isReactCreateElementCall(parent, scopes)) return true;
|
|
43837
|
+
if (isCreateElementCall(parent) || isCloneElementCall(parent)) return true;
|
|
44434
43838
|
valueNode = parent;
|
|
44435
43839
|
parent = parent.parent;
|
|
44436
43840
|
continue;
|
|
44437
|
-
case "VariableDeclarator":
|
|
44438
|
-
|
|
44439
|
-
const
|
|
44440
|
-
|
|
44441
|
-
const nextVisitedSymbols = new Set(visitedSymbols);
|
|
44442
|
-
nextVisitedSymbols.add(aliasSymbol.id);
|
|
44443
|
-
return aliasSymbol.references.some((reference) => reference.flag === "read" && isRenderFlowingReadReference(reference.identifier, scopes, nextVisitedSymbols));
|
|
43841
|
+
case "VariableDeclarator": return parent.init === valueNode && isNodeOfType(parent.id, "Identifier") && isReactComponentName(parent.id.name);
|
|
43842
|
+
case "AssignmentExpression": {
|
|
43843
|
+
const assignmentTarget = parent.left;
|
|
43844
|
+
return parent.right === valueNode && isNodeOfType(assignmentTarget, "Identifier") && isReactComponentName(assignmentTarget.name);
|
|
44444
43845
|
}
|
|
44445
|
-
case "AssignmentExpression": return false;
|
|
44446
43846
|
case "Property":
|
|
44447
43847
|
if (parent.value !== valueNode) return false;
|
|
44448
43848
|
valueNode = parent;
|
|
@@ -44480,7 +43880,6 @@ const noUnstableNestedComponents = defineRule({
|
|
|
44480
43880
|
severity: "warn",
|
|
44481
43881
|
recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
|
|
44482
43882
|
category: "Performance",
|
|
44483
|
-
tags: ["react-jsx-only"],
|
|
44484
43883
|
create: (context) => {
|
|
44485
43884
|
const settings = resolveSettings$8(context.settings);
|
|
44486
43885
|
const renderPropRegex = compileGlob(settings.propNamePattern);
|
|
@@ -44543,7 +43942,7 @@ const noUnstableNestedComponents = defineRule({
|
|
|
44543
43942
|
},
|
|
44544
43943
|
Identifier(node) {
|
|
44545
43944
|
if (!isReactComponentName(node.name)) return;
|
|
44546
|
-
if (!isRenderFlowingReadReference(node
|
|
43945
|
+
if (!isRenderFlowingReadReference(node)) return;
|
|
44547
43946
|
recordInstantiation(node, node.name);
|
|
44548
43947
|
},
|
|
44549
43948
|
FunctionDeclaration: checkFunctionLike,
|
|
@@ -44552,33 +43951,28 @@ const noUnstableNestedComponents = defineRule({
|
|
|
44552
43951
|
ClassDeclaration(node) {
|
|
44553
43952
|
if (!node.id) return;
|
|
44554
43953
|
if (!isReactComponentName(node.id.name)) return;
|
|
44555
|
-
if (!isReactClassComponent(node
|
|
43954
|
+
if (!isReactClassComponent(node)) return;
|
|
44556
43955
|
enqueueCandidate(node, null);
|
|
44557
43956
|
},
|
|
44558
43957
|
ClassExpression(node) {
|
|
44559
43958
|
const inferredName = node.id?.name ?? inferFunctionLikeName(node);
|
|
44560
43959
|
if (!inferredName || !isReactComponentName(inferredName)) return;
|
|
44561
|
-
if (!isReactClassComponent(node
|
|
43960
|
+
if (!isReactClassComponent(node)) return;
|
|
44562
43961
|
enqueueCandidate(node, null);
|
|
44563
43962
|
},
|
|
44564
43963
|
CallExpression(node) {
|
|
44565
|
-
if (
|
|
43964
|
+
if (isCreateElementCall(node)) {
|
|
44566
43965
|
const firstArgument = node.arguments[0];
|
|
44567
43966
|
if (firstArgument && isNodeOfType(firstArgument, "Identifier")) recordInstantiation(firstArgument, firstArgument.name);
|
|
44568
43967
|
else if (firstArgument && isNodeOfType(firstArgument, "MemberExpression")) recordMemberChainInstantiation(firstArgument);
|
|
44569
43968
|
}
|
|
44570
|
-
|
|
44571
|
-
if (!
|
|
44572
|
-
|
|
44573
|
-
const inferredName = inferFunctionLikeName(node);
|
|
44574
|
-
const propInfo = isComponentDeclaredInProp(node);
|
|
44575
|
-
if (propInfo === null && (!inferredName || !isReactComponentName(inferredName))) return;
|
|
44576
|
-
enqueueCandidate(node, propInfo === null ? inferredName : null);
|
|
43969
|
+
if (!isHocCallee$1(node)) return;
|
|
43970
|
+
if (!hocCallContainsComponent(node)) return;
|
|
43971
|
+
enqueueCandidate(node, null);
|
|
44577
43972
|
},
|
|
44578
43973
|
"Program:exit"() {
|
|
44579
43974
|
for (const report of queuedReports) {
|
|
44580
43975
|
if (report.requiredInstantiationName !== null) {
|
|
44581
|
-
if ((report.requiredInstantiationBinding ? context.scopes.symbolFor(report.requiredInstantiationBinding) : null)?.references.some((reference) => reference.flag === "write" || reference.flag === "read-write")) continue;
|
|
44582
43976
|
if (!(report.requiredInstantiationBinding !== null ? instantiatedBindingIdentifiers.has(report.requiredInstantiationBinding) : instantiatedComponentNames.has(report.requiredInstantiationName))) continue;
|
|
44583
43977
|
}
|
|
44584
43978
|
context.report({
|
|
@@ -45611,7 +45005,7 @@ const findChildrenDestructuringFunction = (identifier, scopes) => {
|
|
|
45611
45005
|
const symbol = scopes.symbolFor(identifier);
|
|
45612
45006
|
if (symbol) {
|
|
45613
45007
|
if (symbol.kind !== "parameter") return null;
|
|
45614
|
-
const declaringFunction = findEnclosingFunction
|
|
45008
|
+
const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
|
|
45615
45009
|
if (declaringFunction && destructuresChildrenAsFirstParam(declaringFunction)) return declaringFunction;
|
|
45616
45010
|
return null;
|
|
45617
45011
|
}
|
|
@@ -45657,7 +45051,7 @@ const isChildrenMemberExpression = (node, scopes) => {
|
|
|
45657
45051
|
const symbol = scopes.symbolFor(propsObject);
|
|
45658
45052
|
if (symbol) {
|
|
45659
45053
|
if (symbol.kind !== "parameter") return false;
|
|
45660
|
-
const declaringFunction = findEnclosingFunction
|
|
45054
|
+
const declaringFunction = findEnclosingFunction(symbol.bindingIdentifier);
|
|
45661
45055
|
return declaringFunction ? isDeclaringFunctionComponentLike(declaringFunction) : false;
|
|
45662
45056
|
}
|
|
45663
45057
|
return hasComponentLikeAncestorFunction(node);
|
|
@@ -46309,7 +45703,7 @@ const doesFunctionReturnsObjectLiteral = (functionNode) => {
|
|
|
46309
45703
|
//#endregion
|
|
46310
45704
|
//#region src/plugin/utils/enclosing-component-or-hook-scope.ts
|
|
46311
45705
|
const enclosingComponentOrHookScope = (startNode, ownScopeFor) => {
|
|
46312
|
-
const functionNode = findEnclosingFunction
|
|
45706
|
+
const functionNode = findEnclosingFunction(startNode);
|
|
46313
45707
|
if (!functionNode) return null;
|
|
46314
45708
|
const displayName = componentOrHookDisplayNameForFunction(functionNode);
|
|
46315
45709
|
if (!displayName) return null;
|
|
@@ -47371,7 +46765,7 @@ const isTanstackQuerySource = (source) => TANSTACK_QUERY_PACKAGE_PATTERN.test(so
|
|
|
47371
46765
|
//#region src/plugin/rules/tanstack-query/query-destructure-result.ts
|
|
47372
46766
|
const isHookReturnForwardingSpread = (objectExpression) => {
|
|
47373
46767
|
const objectParent = objectExpression.parent;
|
|
47374
|
-
const enclosingFunction = findEnclosingFunction
|
|
46768
|
+
const enclosingFunction = findEnclosingFunction(objectExpression);
|
|
47375
46769
|
if (!enclosingFunction) return false;
|
|
47376
46770
|
if (!(isNodeOfType(objectParent, "ReturnStatement") || isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && enclosingFunction.body === objectExpression)) return false;
|
|
47377
46771
|
const enclosingName = componentOrHookDisplayNameForFunction(enclosingFunction);
|
|
@@ -47749,10 +47143,10 @@ const hasSuspensionBefore = (functionNode, boundary, context) => {
|
|
|
47749
47143
|
return hasSuspension;
|
|
47750
47144
|
};
|
|
47751
47145
|
const isFunctionAncestor = (ancestor, functionNode) => {
|
|
47752
|
-
let enclosingFunction = findEnclosingFunction
|
|
47146
|
+
let enclosingFunction = findEnclosingFunction(functionNode);
|
|
47753
47147
|
while (enclosingFunction) {
|
|
47754
47148
|
if (enclosingFunction === ancestor) return true;
|
|
47755
|
-
enclosingFunction = findEnclosingFunction
|
|
47149
|
+
enclosingFunction = findEnclosingFunction(enclosingFunction);
|
|
47756
47150
|
}
|
|
47757
47151
|
return false;
|
|
47758
47152
|
};
|
|
@@ -47774,7 +47168,7 @@ const functionInvokesTarget = (callerFunction, targetFunction, context, visitedF
|
|
|
47774
47168
|
return invokesTarget;
|
|
47775
47169
|
};
|
|
47776
47170
|
const isFunctionInvokedBefore = (invokedFunction, boundary, context) => {
|
|
47777
|
-
const boundaryFunction = findEnclosingFunction
|
|
47171
|
+
const boundaryFunction = findEnclosingFunction(boundary);
|
|
47778
47172
|
const boundaryStart = getRangeStart(boundary);
|
|
47779
47173
|
if (!boundaryFunction || boundaryStart === null) return false;
|
|
47780
47174
|
let isInvokedBefore = false;
|
|
@@ -47811,11 +47205,11 @@ const isFunctionInvokedAfter = (invokedFunction, boundary, callerFunction, conte
|
|
|
47811
47205
|
const isWriteExecutedBefore = (writeNode, boundary, context, deferredExecutionFunction) => {
|
|
47812
47206
|
const writeStart = getRangeStart(writeNode);
|
|
47813
47207
|
const boundaryStart = getRangeStart(boundary);
|
|
47814
|
-
const writeFunction = findEnclosingFunction
|
|
47208
|
+
const writeFunction = findEnclosingFunction(writeNode);
|
|
47815
47209
|
const writeExecutionBoundary = writeFunction ?? findProgramRoot(writeNode);
|
|
47816
47210
|
if (writeStart === null || boundaryStart === null || !writeExecutionBoundary || !isNodeReachableWithinFunction(writeNode, context) || !isUnconditionallyExecuted(writeNode, writeExecutionBoundary, context)) return false;
|
|
47817
|
-
const boundaryFunction = findEnclosingFunction
|
|
47818
|
-
const renderFunction = deferredExecutionFunction ? findEnclosingFunction
|
|
47211
|
+
const boundaryFunction = findEnclosingFunction(boundary);
|
|
47212
|
+
const renderFunction = deferredExecutionFunction ? findEnclosingFunction(deferredExecutionFunction) : null;
|
|
47819
47213
|
if (writeFunction === boundaryFunction) return writeStart < boundaryStart;
|
|
47820
47214
|
if (!writeFunction) return writeStart < boundaryStart;
|
|
47821
47215
|
if (boundaryFunction && isFunctionAncestor(writeFunction, boundaryFunction)) {
|
|
@@ -48087,8 +47481,8 @@ const queryStableQueryClient = defineRule({
|
|
|
48087
47481
|
create: (context) => ({ NewExpression(node) {
|
|
48088
47482
|
if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "QueryClient") return;
|
|
48089
47483
|
if (isStableHookWrapperArgument(node)) return;
|
|
48090
|
-
let enclosingFunction = findEnclosingFunction
|
|
48091
|
-
while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction
|
|
47484
|
+
let enclosingFunction = findEnclosingFunction(node);
|
|
47485
|
+
while (enclosingFunction && isImmediatelyInvokedFunction(enclosingFunction)) enclosingFunction = findEnclosingFunction(enclosingFunction);
|
|
48092
47486
|
if (!enclosingFunction) return;
|
|
48093
47487
|
if (!isComponentFunction(enclosingFunction)) return;
|
|
48094
47488
|
context.report({
|
|
@@ -48183,7 +47577,7 @@ const reactCompilerNoManualMemoization = defineRule({
|
|
|
48183
47577
|
const comparatorArgument = node.arguments?.[1];
|
|
48184
47578
|
if (comparatorArgument && !isNullishComparatorArgument(comparatorArgument)) return;
|
|
48185
47579
|
} else {
|
|
48186
|
-
const enclosingFunction = findEnclosingFunction
|
|
47580
|
+
const enclosingFunction = findEnclosingFunction(node);
|
|
48187
47581
|
if (!enclosingFunction || !isCompilerInferableFunction(enclosingFunction)) return;
|
|
48188
47582
|
}
|
|
48189
47583
|
const removalMessage = REMOVAL_MESSAGE_BY_REACT_API_NAME.get(apiName);
|
|
@@ -51288,7 +50682,7 @@ const isReactNativeDimensionsCallee = (node, callee) => {
|
|
|
51288
50682
|
};
|
|
51289
50683
|
const STYLE_FACTORY_CALLEE_PATTERN$1 = /(?:^|\.)(?:make|create)(?:Use)?Styles$/;
|
|
51290
50684
|
const isInsideStyleFactoryCallback = (node) => {
|
|
51291
|
-
const enclosingFunction = findEnclosingFunction
|
|
50685
|
+
const enclosingFunction = findEnclosingFunction(node);
|
|
51292
50686
|
if (!enclosingFunction) return false;
|
|
51293
50687
|
const callExpression = enclosingFunction.parent;
|
|
51294
50688
|
if (!callExpression || !isNodeOfType(callExpression, "CallExpression")) return false;
|
|
@@ -58144,14 +57538,12 @@ const declarationAwaitsRequestScopedCall = (declaration) => {
|
|
|
58144
57538
|
}
|
|
58145
57539
|
return false;
|
|
58146
57540
|
};
|
|
58147
|
-
const declarationAwaitsGate = (declaration
|
|
57541
|
+
const declarationAwaitsGate = (declaration) => {
|
|
58148
57542
|
if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
|
|
58149
57543
|
for (const declarator of declaration.declarations ?? []) {
|
|
58150
57544
|
if (!isNodeOfType(declarator.init, "AwaitExpression")) continue;
|
|
58151
57545
|
const argument = declarator.init.argument;
|
|
58152
57546
|
if (!isNodeOfType(argument, "CallExpression")) continue;
|
|
58153
|
-
if (hasPossibleStaticMemberCallWrite(argument, context.scopes)) return true;
|
|
58154
|
-
if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) continue;
|
|
58155
57547
|
const calleeName = getCalleeName$2(argument);
|
|
58156
57548
|
if (!calleeName) continue;
|
|
58157
57549
|
if (isAuthGuardName(calleeName)) return true;
|
|
@@ -58179,7 +57571,7 @@ const serverSequentialIndependentAwait = defineRule({
|
|
|
58179
57571
|
if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
|
|
58180
57572
|
if (declarationAwaitsExistingPromise(nextStatement)) continue;
|
|
58181
57573
|
if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
|
|
58182
|
-
if (declarationAwaitsGate(currentStatement
|
|
57574
|
+
if (declarationAwaitsGate(currentStatement)) continue;
|
|
58183
57575
|
context.report({
|
|
58184
57576
|
node: nextStatement,
|
|
58185
57577
|
message: "This await doesn't use the previous result, so your users wait twice as long for nothing."
|
|
@@ -59165,7 +58557,7 @@ const tanstackStartNoNavigateInRender = defineRule({
|
|
|
59165
58557
|
const isReturnedFromCustomHook = (functionNode) => {
|
|
59166
58558
|
const parent = functionNode.parent;
|
|
59167
58559
|
if (isNodeOfType(parent, "ReturnStatement")) {
|
|
59168
|
-
const outerFunction = findEnclosingFunction
|
|
58560
|
+
const outerFunction = findEnclosingFunction(parent);
|
|
59169
58561
|
const hookName = outerFunction ? getFunctionBindingName$1(outerFunction) : null;
|
|
59170
58562
|
return Boolean(hookName && HOOK_NAME_PATTERN$3.test(hookName));
|
|
59171
58563
|
}
|
|
@@ -59182,13 +58574,13 @@ const tanstackStartNoNavigateInRender = defineRule({
|
|
|
59182
58574
|
return parent.callee === functionNode || (parent.arguments ?? []).some((callArgument) => callArgument === functionNode);
|
|
59183
58575
|
};
|
|
59184
58576
|
const isDeferredNavigateCall = (callNode) => {
|
|
59185
|
-
let enclosingFunction = findEnclosingFunction
|
|
58577
|
+
let enclosingFunction = findEnclosingFunction(callNode);
|
|
59186
58578
|
while (enclosingFunction) {
|
|
59187
58579
|
if (isDeferredCallbackPosition(enclosingFunction)) return true;
|
|
59188
58580
|
if (isWiredAsEventHandler(enclosingFunction)) return true;
|
|
59189
58581
|
if (isReturnedFromCustomHook(enclosingFunction)) return true;
|
|
59190
58582
|
if (!isSynchronouslyInvokedAnonymousWrapper(enclosingFunction)) return false;
|
|
59191
|
-
enclosingFunction = findEnclosingFunction
|
|
58583
|
+
enclosingFunction = findEnclosingFunction(enclosingFunction);
|
|
59192
58584
|
}
|
|
59193
58585
|
return false;
|
|
59194
58586
|
};
|