Skip to content

Commit f84eb96

Browse files
authoredNov 30, 2019
feat(eslint-plugin): [prefer-null-coal] opt for suggestion fixer (#1272)
1 parent dc73510 commit f84eb96

File tree

3 files changed

+79
-25
lines changed

3 files changed

+79
-25
lines changed
 

‎packages/eslint-plugin/docs/rules/prefer-nullish-coalescing.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ type Options = [
4646
{
4747
ignoreConditionalTests?: boolean;
4848
ignoreMixedLogicalExpressions?: boolean;
49+
forceSuggestionFixer?: boolean;
4950
},
5051
];
5152

5253
const defaultOptions = [
5354
{
5455
ignoreConditionalTests: true,
55-
ignoreMixedLogicalExpressions: true;
56+
ignoreMixedLogicalExpressions: true,
57+
forceSuggestionFixer: false,
5658
},
5759
];
5860
```
@@ -129,7 +131,13 @@ a ?? (b && c) ?? d;
129131
a ?? (b && c && d);
130132
```
131133

132-
**_NOTE:_** Errors for this specific case will be presented as suggestions, instead of fixes. This is because it is not always safe to automatically convert `||` to `??` within a mixed logical expression, as we cannot tell the intended precedence of the operator. Note that by design, `??` requires parentheses when used with `&&` or `||` in the same expression.
134+
**_NOTE:_** Errors for this specific case will be presented as suggestions (see below), instead of fixes. This is because it is not always safe to automatically convert `||` to `??` within a mixed logical expression, as we cannot tell the intended precedence of the operator. Note that by design, `??` requires parentheses when used with `&&` or `||` in the same expression.
135+
136+
### forceSuggestionFixer
137+
138+
Setting this option to `true` will cause the rule to use ESLint's "suggested fix" mode for all fixes. _This option is provided as to aid in transitioning your codebase onto this rule_.
139+
140+
Suggestion fixes cannot be automatically applied via the `--fix` CLI command, but can be _manually_ chosen to be applied one at a time via an IDE or similar. This makes it safe to run autofixers on an existing codebase without worrying about potential runtime behaviour changes from this rule's fixer.
133141

134142
## When Not To Use It
135143

‎packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts

+41-23
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type Options = [
1111
{
1212
ignoreConditionalTests?: boolean;
1313
ignoreMixedLogicalExpressions?: boolean;
14+
forceSuggestionFixer?: boolean;
1415
},
1516
];
1617
export type MessageIds = 'preferNullish';
@@ -41,6 +42,9 @@ export default util.createRule<Options, MessageIds>({
4142
ignoreMixedLogicalExpressions: {
4243
type: 'boolean',
4344
},
45+
forceSuggestionFixer: {
46+
type: 'boolean',
47+
},
4448
},
4549
additionalProperties: false,
4650
},
@@ -50,9 +54,19 @@ export default util.createRule<Options, MessageIds>({
5054
{
5155
ignoreConditionalTests: true,
5256
ignoreMixedLogicalExpressions: true,
57+
forceSuggestionFixer: false,
5358
},
5459
],
55-
create(context, [{ ignoreConditionalTests, ignoreMixedLogicalExpressions }]) {
60+
create(
61+
context,
62+
[
63+
{
64+
ignoreConditionalTests,
65+
ignoreMixedLogicalExpressions,
66+
forceSuggestionFixer,
67+
},
68+
],
69+
) {
5670
const parserServices = util.getParserServices(context);
5771
const sourceCode = context.getSourceCode();
5872
const checker = parserServices.program.getTypeChecker();
@@ -79,30 +93,34 @@ export default util.createRule<Options, MessageIds>({
7993
return;
8094
}
8195

82-
const barBarOperator = sourceCode.getTokenAfter(
83-
node.left,
84-
token =>
85-
token.type === AST_TOKEN_TYPES.Punctuator &&
86-
token.value === node.operator,
87-
)!; // there _must_ be an operator
88-
89-
const fixer = isMixedLogical
90-
? // suggestion instead for cases where we aren't sure if the fixer is completely safe
91-
({
92-
suggest: [
93-
{
94-
messageId: 'preferNullish',
95-
fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix {
96-
return fixer.replaceText(barBarOperator, '??');
96+
const barBarOperator = util.nullThrows(
97+
sourceCode.getTokenAfter(
98+
node.left,
99+
token =>
100+
token.type === AST_TOKEN_TYPES.Punctuator &&
101+
token.value === node.operator,
102+
),
103+
util.NullThrowsReasons.MissingToken('operator', node.type),
104+
);
105+
106+
const fixer =
107+
isMixedLogical || forceSuggestionFixer
108+
? // suggestion instead for cases where we aren't sure if the fixer is completely safe
109+
({
110+
suggest: [
111+
{
112+
messageId: 'preferNullish',
113+
fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix {
114+
return fixer.replaceText(barBarOperator, '??');
115+
},
97116
},
117+
],
118+
} as const)
119+
: {
120+
fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix {
121+
return fixer.replaceText(barBarOperator, '??');
98122
},
99-
],
100-
} as const)
101-
: {
102-
fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix {
103-
return fixer.replaceText(barBarOperator, '??');
104-
},
105-
};
123+
};
106124

107125
context.report({
108126
node: barBarOperator,

‎packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts

+28
Original file line numberDiff line numberDiff line change
@@ -435,5 +435,33 @@ if (function werid() { return x ?? 'foo' }) {}
435435
},
436436
],
437437
})),
438+
439+
// testing the suggestion fixer option
440+
{
441+
code: `
442+
declare const x: string | null;
443+
x || 'foo';
444+
`.trimRight(),
445+
output: null,
446+
options: [{ forceSuggestionFixer: true }],
447+
errors: [
448+
{
449+
messageId: 'preferNullish',
450+
line: 3,
451+
column: 3,
452+
endLine: 3,
453+
endColumn: 5,
454+
suggestions: [
455+
{
456+
messageId: 'preferNullish',
457+
output: `
458+
declare const x: string | null;
459+
x ?? 'foo';
460+
`.trimRight(),
461+
},
462+
],
463+
},
464+
],
465+
},
438466
],
439467
});

0 commit comments

Comments
 (0)
Please sign in to comment.