Skip to content

Commit 343824f

Browse files
authored
Fix fixSpaceAroundKeyword (#1451)
1 parent fce9d1d commit 343824f

File tree

7 files changed

+62
-183
lines changed

7 files changed

+62
-183
lines changed

rules/fix/fix-space-around-keywords.js

Lines changed: 27 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,36 @@
11
'use strict';
2-
3-
const isKeywordToken = keyword => ({type, value}) => type === 'Keyword' && value === keyword;
2+
const {getParenthesizedRange} = require('../utils/parentheses.js');
3+
4+
const isProblematicToken = ({type, value}) => {
5+
return (
6+
(type === 'Keyword' && /^[a-z]*$/.test(value)) ||
7+
// ForOfStatement
8+
(type === 'Identifier' && value === 'of') ||
9+
// AwaitExpression
10+
(type === 'Identifier' && value === 'await')
11+
);
12+
};
413

514
function * fixSpaceAroundKeyword(fixer, node, sourceCode) {
6-
const {parent} = node;
7-
const keywords = [];
8-
9-
switch (parent.type) {
10-
case 'YieldExpression':
11-
if (parent.delegate) {
12-
break;
13-
}
14-
// Fallthrough
15-
16-
case 'ReturnStatement':
17-
case 'ThrowStatement':
18-
case 'AwaitExpression': {
19-
/* istanbul ignore else */
20-
if (parent.argument === node) {
21-
keywords.push({
22-
keyword: sourceCode.getFirstToken(parent),
23-
side: 'after',
24-
});
25-
}
26-
27-
break;
28-
}
29-
30-
case 'UnaryExpression': {
31-
const {operator, prefix, argument: unaryExpressionArgument} = parent;
32-
/* istanbul ignore else */
33-
if (
34-
(
35-
operator === 'typeof' ||
36-
operator === 'void' ||
37-
operator === 'delete'
38-
) &&
39-
prefix &&
40-
unaryExpressionArgument === node
41-
) {
42-
keywords.push({
43-
keyword: sourceCode.getFirstToken(parent),
44-
side: 'after',
45-
});
46-
}
47-
48-
break;
49-
}
50-
51-
case 'BinaryExpression': {
52-
const {operator, left} = parent;
53-
/* istanbul ignore else */
54-
if (
55-
operator === 'in' ||
56-
operator === 'instanceof'
57-
) {
58-
keywords.push({
59-
keyword: sourceCode.getTokenAfter(left, {filter: isKeywordToken(operator)}),
60-
side: left === node ? 'before' : 'after',
61-
});
62-
}
63-
64-
break;
65-
}
66-
67-
case 'ExportDefaultDeclaration': {
68-
/* istanbul ignore else */
69-
if (parent.declaration === node) {
70-
keywords.push({
71-
keyword: sourceCode.getFirstToken(parent, {filter: isKeywordToken('default')}),
72-
side: 'after',
73-
});
74-
}
75-
76-
break;
77-
}
78-
79-
case 'ExpressionStatement': {
80-
/* istanbul ignore else */
81-
if (parent.expression === node) {
82-
yield * fixSpaceAroundKeyword(fixer, parent, sourceCode);
83-
return;
84-
}
85-
86-
/* istanbul ignore next */
87-
break;
88-
}
89-
90-
case 'IfStatement': {
91-
/* istanbul ignore else */
92-
if (parent.alternate === node) {
93-
keywords.push({
94-
keyword: sourceCode.getTokenBefore(node, {filter: isKeywordToken('else')}),
95-
side: 'after',
96-
});
97-
}
98-
99-
break;
100-
}
101-
102-
case 'DoWhileStatement': {
103-
/* istanbul ignore else */
104-
if (parent.body === node) {
105-
keywords.push({
106-
keyword: sourceCode.getFirstToken(parent),
107-
side: 'after',
108-
});
109-
}
110-
111-
break;
112-
}
113-
114-
case 'SwitchCase': {
115-
/* istanbul ignore else */
116-
if (parent.test === node) {
117-
keywords.push({
118-
keyword: sourceCode.getTokenBefore(node, {filter: isKeywordToken('case')}),
119-
side: 'after',
120-
});
121-
}
122-
123-
break;
124-
}
125-
126-
case 'VariableDeclarator': {
127-
const grandParent = parent.parent;
128-
if (
129-
grandParent.type === 'VariableDeclaration' &&
130-
grandParent.declarations[0] === parent
131-
) {
132-
keywords.push({
133-
keyword: sourceCode.getFirstToken(grandParent),
134-
side: 'after',
135-
});
136-
}
137-
138-
break;
139-
}
140-
141-
case 'ForOfStatement': {
142-
// Note: Other keywords and children not handled, because not using
143-
if (parent.right === node) {
144-
keywords.push({
145-
keyword: sourceCode.getTokenBefore(node, {filter: ({type, value}) => type === 'Identifier' && value === 'of'}),
146-
side: 'after',
147-
});
148-
}
149-
150-
break;
151-
}
152-
153-
case 'ForInStatement': {
154-
// Note: Other keywords and children not handled, because not using
155-
if (parent.right === node) {
156-
keywords.push({
157-
keyword: sourceCode.getTokenBefore(node, {filter: isKeywordToken('in')}),
158-
side: 'after',
159-
});
160-
}
161-
162-
break;
163-
}
164-
165-
case 'ClassDeclaration':
166-
case 'ClassExpression': {
167-
/* istanbul ignore else */
168-
if (parent.superClass === node) {
169-
keywords.push({
170-
keyword: sourceCode.getTokenBefore(node, {filter: isKeywordToken('extends')}),
171-
side: 'after',
172-
});
173-
}
174-
175-
break;
176-
}
177-
178-
// No default
15+
const range = getParenthesizedRange(node, sourceCode);
16+
const tokenBefore = sourceCode.getTokenBefore({range}, {includeComments: true});
17+
18+
if (
19+
tokenBefore &&
20+
range[0] === tokenBefore.range[1] &&
21+
isProblematicToken(tokenBefore)
22+
) {
23+
yield fixer.insertTextAfter(tokenBefore, ' ');
17924
}
18025

181-
for (const {keyword, side} of keywords) {
182-
const characterIndex = side === 'before' ?
183-
keyword.range[0] - 1 :
184-
keyword.range[1];
26+
const tokenAfter = sourceCode.getTokenAfter({range}, {includeComments: true});
18527

186-
if (sourceCode.text.charAt(characterIndex) !== ' ') {
187-
yield fixer[side === 'before' ? 'insertTextBefore' : 'insertTextAfter'](keyword, ' ');
188-
}
28+
if (
29+
tokenAfter &&
30+
range[1] === tokenAfter.range[0] &&
31+
isProblematicToken(tokenAfter)
32+
) {
33+
yield fixer.insertTextBefore(tokenAfter, ' ');
18934
}
19035
}
19136

test/no-zero-fractions.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ test.snapshot({
8484
'a = .0.toString()',
8585
'function foo(){return.0}',
8686
'function foo(){return.0.toString()}',
87+
'function foo(){return.0+.1}',
8788
outdent`
8889
console.log()
8990
.0.toString()

test/prefer-array-flat.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ test.snapshot({
2424
invalid: [
2525
'array.flatMap(x => x)',
2626
'function foo(){return[].flatMap(x => x)}',
27+
'foo.flatMap(x => x)instanceof Array',
2728
],
2829
});
2930

test/snapshots/no-zero-fractions.mjs.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2742,7 +2742,7 @@ Generated by [AVA](https://avajs.dev).
27422742
> Output
27432743
27442744
`␊
2745-
1 | function foo(){return(0).toString()}␊
2745+
1 | function foo(){return (0).toString()}␊
27462746
`
27472747

27482748
> Error 1/1
@@ -2753,6 +2753,22 @@ Generated by [AVA](https://avajs.dev).
27532753
`
27542754

27552755
## Invalid #172
2756+
1 | function foo(){return.0+.1}
2757+
2758+
> Output
2759+
2760+
`␊
2761+
1 | function foo(){return 0+.1}␊
2762+
`
2763+
2764+
> Error 1/1
2765+
2766+
`␊
2767+
> 1 | function foo(){return.0+.1}␊
2768+
| ^ Don't use a zero fraction in the number.␊
2769+
`
2770+
2771+
## Invalid #173
27562772
1 | console.log()
27572773
2 | .0.toString()
27582774

44 Bytes
Binary file not shown.

test/snapshots/prefer-array-flat.mjs.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ Generated by [AVA](https://avajs.dev).
3636
| ^^^^^^^^^^^^^^^^^^ Prefer \`Array#flat()\` over \`Array#flatMap()\` to flatten an array.␊
3737
`
3838

39+
## Invalid #3
40+
1 | foo.flatMap(x => x)instanceof Array
41+
42+
> Output
43+
44+
`␊
45+
1 | foo.flat() instanceof Array␊
46+
`
47+
48+
> Error 1/1
49+
50+
`␊
51+
> 1 | foo.flatMap(x => x)instanceof Array␊
52+
| ^^^^^^^^^^^^^^^^^^^ Prefer \`Array#flat()\` over \`Array#flatMap()\` to flatten an array.␊
53+
`
54+
3955
## Invalid #1
4056
1 | array.reduce((a, b) => a.concat(b), [])
4157

59 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)