|
1 | 1 | '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 | +}; |
4 | 13 |
|
5 | 14 | 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, ' '); |
179 | 24 | }
|
180 | 25 |
|
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}); |
185 | 27 |
|
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, ' '); |
189 | 34 | }
|
190 | 35 | }
|
191 | 36 |
|
|
0 commit comments