Skip to content

Commit e54dc66

Browse files
authored
prefer-spread: Improve auto-fix (#1080)
1 parent da94ca9 commit e54dc66

File tree

4 files changed

+478
-15
lines changed

4 files changed

+478
-15
lines changed

rules/prefer-spread.js

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const methodSelector = require('./utils/method-selector');
55
const needsSemicolon = require('./utils/needs-semicolon');
66
const getParentheses = require('./utils/get-parentheses');
77
const shouldAddParenthesesToSpreadElementArgument = require('./utils/should-add-parentheses-to-spread-element-argument');
8+
const replaceNodeOrTokenAndSpacesBefore = require('./utils/replace-node-or-token-and-spaces-before');
9+
const removeSpacesAfter = require('./utils/remove-spaces-after');
810

911
const ERROR_ARRAY_FROM = 'array-from';
1012
const ERROR_ARRAY_CONCAT = 'array-concat';
@@ -230,28 +232,63 @@ function getConcatFixableArguments(argumentsList, scope) {
230232
return fixableArguments;
231233
}
232234

235+
function fixArrayFrom(node, sourceCode) {
236+
const [object] = node.arguments;
237+
238+
function getObjectText() {
239+
if (isArrayLiteral(object)) {
240+
return sourceCode.getText(object);
241+
}
242+
243+
const [start, end] = getParenthesizedRange(object, sourceCode);
244+
let text = sourceCode.text.slice(start, end);
245+
246+
if (
247+
!isParenthesized(object, sourceCode) &&
248+
shouldAddParenthesesToSpreadElementArgument(object)
249+
) {
250+
text = `(${text})`;
251+
}
252+
253+
return `[...${text}]`;
254+
}
255+
256+
function * removeObject(fixer) {
257+
yield * replaceNodeOrTokenAndSpacesBefore(object, '', fixer, sourceCode);
258+
const commaToken = sourceCode.getTokenAfter(object, isCommaToken);
259+
yield * replaceNodeOrTokenAndSpacesBefore(commaToken, '', fixer, sourceCode);
260+
yield removeSpacesAfter(commaToken, sourceCode, fixer);
261+
}
262+
263+
return function * (fixer) {
264+
// Fixed code always starts with `[`
265+
if (needsSemicolon(sourceCode.getTokenBefore(node), sourceCode, '[')) {
266+
yield fixer.insertTextBefore(node, ';');
267+
}
268+
269+
const objectText = getObjectText();
270+
271+
if (node.arguments.length === 1) {
272+
yield fixer.replaceText(node, objectText);
273+
return;
274+
}
275+
276+
// `Array.from(object, mapFunction, thisArgument)` -> `[...object].map(mapFunction, thisArgument)`
277+
yield fixer.replaceText(node.callee.object, objectText);
278+
yield fixer.replaceText(node.callee.property, 'map');
279+
yield * removeObject(fixer);
280+
};
281+
}
282+
233283
const create = context => {
234284
const sourceCode = context.getSourceCode();
235-
const getSource = node => sourceCode.getText(node);
236285

237286
return {
238287
[arrayFromCallSelector](node) {
239288
context.report({
240289
node,
241290
messageId: ERROR_ARRAY_FROM,
242-
fix: fixer => {
243-
const [arrayLikeArgument, mapFn, thisArgument] = node.arguments.map(node => getSource(node));
244-
let replacement = `${
245-
needsSemicolon(sourceCode.getTokenBefore(node), sourceCode) ? ';' : ''
246-
}[...${arrayLikeArgument}]`;
247-
248-
if (mapFn) {
249-
const mapArguments = [mapFn, thisArgument].filter(Boolean);
250-
replacement += `.map(${mapArguments.join(', ')})`;
251-
}
252-
253-
return fixer.replaceText(node, replacement);
254-
}
291+
fix: fixArrayFrom(node, sourceCode)
255292
});
256293
},
257294
[arrayConcatCallSelector](node) {

test/prefer-spread.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,33 @@ test.snapshot({
130130
outdent`
131131
const foo = {}
132132
Array.from(arrayLike).forEach(doSomething)
133-
`
133+
`,
134+
135+
'(Array).from(foo)',
136+
'(Array.from)(foo)',
137+
'((Array).from)(foo)',
138+
'(Array).from((0, foo))',
139+
'(Array.from)((0, foo))',
140+
'((Array).from)((0, foo))',
141+
'(Array).from(foo, bar)',
142+
'(Array.from)(foo, bar)',
143+
'((Array).from)(foo, bar)',
144+
'(Array).from((0, foo), bar)',
145+
'(Array.from)((0, foo), bar)',
146+
'((Array).from)((0, foo), bar)',
147+
'(Array).from(foo, bar, baz)',
148+
'(Array.from)(foo, bar, baz)',
149+
'((Array).from)(foo, bar, baz)',
150+
'(Array).from((0, foo), bar, baz)',
151+
'(Array.from)((0, foo), bar, baz)',
152+
'((Array).from)((0, foo), bar, baz)',
153+
'Array.from(a, (0, bar), (0, baz),)',
154+
'Array.from(a ? b : c)',
155+
'Array.from([...a, ...b], b, c)',
156+
'Array.from([1])',
157+
'Array.from([...a, ...b])',
158+
'/* 1 */ Array /* 2 */ .from /* 3 */ ( /* 4 */ a /* 5 */, /* 6 */ b /* 7 */, /* 8 */ c /* 9 */,)',
159+
'/* 1 */ Array /* 2 */ .from /* 3 */ ( /* 4 */ a /* 5 */,)'
134160
]
135161
});
136162

0 commit comments

Comments
 (0)