Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1960131

Browse files
authored
Add opt-in support for dangerous autofix (facebook#18437)
1 parent 90e90ac commit 1960131

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6336,6 +6336,34 @@ const tests = {
63366336
},
63376337
],
63386338
},
6339+
{
6340+
code: normalizeIndent`
6341+
function MyComponent() {
6342+
const local = {};
6343+
useEffect(() => {
6344+
console.log(local);
6345+
}, []);
6346+
}
6347+
`,
6348+
// Dangerous autofix is enabled due to the option:
6349+
output: normalizeIndent`
6350+
function MyComponent() {
6351+
const local = {};
6352+
useEffect(() => {
6353+
console.log(local);
6354+
}, [local]);
6355+
}
6356+
`,
6357+
errors: [
6358+
{
6359+
message:
6360+
"React Hook useEffect has a missing dependency: 'local'. " +
6361+
'Either include it or remove the dependency array.',
6362+
},
6363+
],
6364+
// Keep this until major IDEs and VS Code FB ESLint plugin support Suggestions API.
6365+
options: [{enableDangerousAutofixThisMayCauseInfiniteLoops: true}],
6366+
},
63396367
],
63406368
};
63416369

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

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,19 @@
1111

1212
export default {
1313
meta: {
14+
fixable: 'code',
1415
schema: [
1516
{
1617
type: 'object',
1718
additionalProperties: false,
19+
enableDangerousAutofixThisMayCauseInfiniteLoops: false,
1820
properties: {
1921
additionalHooks: {
2022
type: 'string',
2123
},
24+
enableDangerousAutofixThisMayCauseInfiniteLoops: {
25+
type: 'boolean',
26+
},
2227
},
2328
},
2429
],
@@ -31,7 +36,28 @@ export default {
3136
context.options[0].additionalHooks
3237
? new RegExp(context.options[0].additionalHooks)
3338
: undefined;
34-
const options = {additionalHooks};
39+
40+
const enableDangerousAutofixThisMayCauseInfiniteLoops =
41+
(context.options &&
42+
context.options[0] &&
43+
context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops) ||
44+
false;
45+
46+
const options = {
47+
additionalHooks,
48+
enableDangerousAutofixThisMayCauseInfiniteLoops,
49+
};
50+
51+
function reportProblem(problem) {
52+
if (enableDangerousAutofixThisMayCauseInfiniteLoops) {
53+
// Used to enable legacy behavior. Dangerous.
54+
// Keep this as an option until major IDEs upgrade (including VSCode FB ESLint extension).
55+
if (Array.isArray(problem.suggest) && problem.suggest.length > 0) {
56+
problem.fix = problem.suggest[0].fix;
57+
}
58+
}
59+
context.report(problem);
60+
}
3561

3662
const scopeManager = context.getSourceCode().scopeManager;
3763

@@ -140,7 +166,7 @@ export default {
140166
break; // Unhandled
141167
default:
142168
// useEffect(generateEffectBody(), []);
143-
context.report({
169+
reportProblem({
144170
node: reactiveHook,
145171
message:
146172
`React Hook ${reactiveHookName} received a function whose dependencies ` +
@@ -150,7 +176,7 @@ export default {
150176
}
151177

152178
// Something unusual. Fall back to suggesting to add the body itself as a dep.
153-
context.report({
179+
reportProblem({
154180
node: reactiveHook,
155181
message:
156182
`React Hook ${reactiveHookName} has a missing dependency: '${callback.name}'. ` +
@@ -190,7 +216,7 @@ export default {
190216
reactiveHookName === 'useCallback'
191217
) {
192218
// TODO: Can this have a suggestion?
193-
context.report({
219+
reportProblem({
194220
node: reactiveHook,
195221
message:
196222
`React Hook ${reactiveHookName} does nothing when called with ` +
@@ -202,7 +228,7 @@ export default {
202228
}
203229

204230
if (isEffect && node.async) {
205-
context.report({
231+
reportProblem({
206232
node: node,
207233
message:
208234
`Effect callbacks are synchronous to prevent race conditions. ` +
@@ -557,7 +583,7 @@ export default {
557583
if (foundCurrentAssignment) {
558584
return;
559585
}
560-
context.report({
586+
reportProblem({
561587
node: dependencyNode.parent.property,
562588
message:
563589
`The ref value '${dependency}.current' will likely have ` +
@@ -577,7 +603,7 @@ export default {
577603
return;
578604
}
579605
staleAssignments.add(key);
580-
context.report({
606+
reportProblem({
581607
node: writeExpr,
582608
message:
583609
`Assignments to the '${key}' variable from inside React Hook ` +
@@ -645,7 +671,7 @@ export default {
645671
externalDependencies: new Set(),
646672
isEffect: true,
647673
});
648-
context.report({
674+
reportProblem({
649675
node: reactiveHook,
650676
message:
651677
`React Hook ${reactiveHookName} contains a call to '${setStateInsideEffectWithoutDeps}'. ` +
@@ -677,7 +703,7 @@ export default {
677703
// If the declared dependencies are not an array expression then we
678704
// can't verify that the user provided the correct dependencies. Tell
679705
// the user this in an error.
680-
context.report({
706+
reportProblem({
681707
node: declaredDependenciesNode,
682708
message:
683709
`React Hook ${context.getSource(reactiveHook)} was passed a ` +
@@ -693,7 +719,7 @@ export default {
693719
}
694720
// If we see a spread element then add a special warning.
695721
if (declaredDependencyNode.type === 'SpreadElement') {
696-
context.report({
722+
reportProblem({
697723
node: declaredDependencyNode,
698724
message:
699725
`React Hook ${context.getSource(reactiveHook)} has a spread ` +
@@ -712,23 +738,23 @@ export default {
712738
if (/Unsupported node type/.test(error.message)) {
713739
if (declaredDependencyNode.type === 'Literal') {
714740
if (dependencies.has(declaredDependencyNode.value)) {
715-
context.report({
741+
reportProblem({
716742
node: declaredDependencyNode,
717743
message:
718744
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
719745
`because it never changes. ` +
720746
`Did you mean to include ${declaredDependencyNode.value} in the array instead?`,
721747
});
722748
} else {
723-
context.report({
749+
reportProblem({
724750
node: declaredDependencyNode,
725751
message:
726752
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
727753
'because it never changes. You can safely remove it.',
728754
});
729755
}
730756
} else {
731-
context.report({
757+
reportProblem({
732758
node: declaredDependencyNode,
733759
message:
734760
`React Hook ${context.getSource(reactiveHook)} has a ` +
@@ -828,7 +854,7 @@ export default {
828854
}
829855
// TODO: What if the function needs to change on every render anyway?
830856
// Should we suggest removing effect deps as an appropriate fix too?
831-
context.report({
857+
reportProblem({
832858
// TODO: Why not report this at the dependency site?
833859
node: fn.node,
834860
message,
@@ -1099,7 +1125,7 @@ export default {
10991125
}
11001126
}
11011127

1102-
context.report({
1128+
reportProblem({
11031129
node: declaredDependenciesNode,
11041130
message:
11051131
`React Hook ${context.getSource(reactiveHook)} has ` +

0 commit comments

Comments
 (0)