Skip to content

Commit a4dd52d

Browse files
author
Pascal Fong Kye
committed
Add and fix tests for new babel eslint parser
1 parent de71e14 commit a4dd52d

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js

+5
Original file line numberDiff line numberDiff line change
@@ -7961,6 +7961,11 @@ new ESLintTester({
79617961
parserOptions,
79627962
}).run('react-hooks', ReactHooksESLintRule, tests);
79637963

7964+
new ESLintTester({
7965+
parser: require.resolve('@babel/eslint-parser'),
7966+
parserOptions,
7967+
}).run('react-hooks', ReactHooksESLintRule, tests);
7968+
79647969
new ESLintTester({
79657970
parser: require.resolve('@typescript-eslint/parser'),
79667971
parserOptions,

packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js

+37-4
Original file line numberDiff line numberDiff line change
@@ -803,9 +803,10 @@ export default {
803803
let maybeID = declaredDependencyNode;
804804
while (
805805
maybeID.type === 'MemberExpression' ||
806-
maybeID.type === 'OptionalMemberExpression'
806+
maybeID.type === 'OptionalMemberExpression' ||
807+
maybeID.type === 'ChainExpression'
807808
) {
808-
maybeID = maybeID.object;
809+
maybeID = maybeID.object || maybeID.expression.object;
809810
}
810811
const isDeclaredInComponent = !componentScope.through.some(
811812
ref => ref.identifier === maybeID,
@@ -1599,8 +1600,19 @@ function analyzePropertyChain(node, optionalChains) {
15991600
const property = analyzePropertyChain(node.property, null);
16001601
const result = `${object}.${property}`;
16011602
if (optionalChains) {
1602-
// Mark as required.
1603-
optionalChains.set(result, false);
1603+
// Note: OptionalMemberExpression doesn't necessarily mean this node is optional.
1604+
// It just means there is an optional member somewhere inside.
1605+
// This particular node might still represent a required member, so check .optional field.
1606+
if (node.optional) {
1607+
// We only want to consider it optional if *all* usages were optional.
1608+
if (!optionalChains.has(result)) {
1609+
// Mark as (maybe) optional. If there's a required usage, this will be overridden.
1610+
optionalChains.set(result, true);
1611+
}
1612+
} else {
1613+
// Mark as required.
1614+
optionalChains.set(result, false);
1615+
}
16041616
}
16051617
return result;
16061618
} else if (node.type === 'OptionalMemberExpression' && !node.computed) {
@@ -1623,6 +1635,27 @@ function analyzePropertyChain(node, optionalChains) {
16231635
}
16241636
}
16251637
return result;
1638+
} else if (node.type === 'ChainExpression' && !node.computed) {
1639+
const expression = node.expression;
1640+
const object = analyzePropertyChain(expression.object, optionalChains);
1641+
const property = analyzePropertyChain(expression.property, null);
1642+
const result = `${object}.${property}`;
1643+
if (optionalChains) {
1644+
// Note: OptionalMemberExpression doesn't necessarily mean this node is optional.
1645+
// It just means there is an optional member somewhere inside.
1646+
// This particular node might still represent a required member, so check .optional field.
1647+
if (expression.optional) {
1648+
// We only want to consider it optional if *all* usages were optional.
1649+
if (!optionalChains.has(result)) {
1650+
// Mark as (maybe) optional. If there's a required usage, this will be overridden.
1651+
optionalChains.set(result, true);
1652+
}
1653+
} else {
1654+
// Mark as required.
1655+
optionalChains.set(result, false);
1656+
}
1657+
}
1658+
return result;
16261659
} else {
16271660
throw new Error(`Unsupported node type: ${node.type}`);
16281661
}

0 commit comments

Comments
 (0)