Skip to content

Commit e556143

Browse files
costajohntfisker
andauthored
prefer-set-size: Add Array.from() support (#2857)
Co-authored-by: fisker <lionkay@gmail.com>
1 parent a332a50 commit e556143

File tree

4 files changed

+208
-15
lines changed

4 files changed

+208
-15
lines changed

rules/prefer-set-size.js

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {findVariable} from '@eslint-community/eslint-utils';
22
import {fixSpaceAroundKeyword} from './fix/index.js';
3-
import {isNewExpression, isMemberExpression} from './ast/index.js';
3+
import {isNewExpression, isMemberExpression, isMethodCall} from './ast/index.js';
44

55
const MESSAGE_ID = 'prefer-set-size';
66
const messages = {
@@ -36,24 +36,49 @@ function isSet(node, scope) {
3636
&& isNewSet(definition.node.init);
3737
}
3838

39-
// `[...set].length` -> `set.size`
40-
function fix(context, lengthAccessNodes) {
39+
function getSetNode(memberExpressionObject) {
40+
// `[...set].length`
41+
if (
42+
memberExpressionObject.type === 'ArrayExpression'
43+
&& memberExpressionObject.elements.length === 1
44+
&& memberExpressionObject.elements[0]?.type === 'SpreadElement'
45+
) {
46+
return memberExpressionObject.elements[0].argument;
47+
}
48+
49+
// `Array.from(set).length`
50+
if (
51+
isMethodCall(memberExpressionObject, {
52+
object: 'Array',
53+
method: 'from',
54+
argumentsLength: 1,
55+
optionalCall: false,
56+
optionalMember: false,
57+
})
58+
) {
59+
return memberExpressionObject.arguments[0];
60+
}
61+
}
62+
63+
function createFix(context, lengthAccessNode, set) {
4164
const {sourceCode} = context;
4265
const {
43-
object: arrayExpression,
66+
object: array,
4467
property,
45-
} = lengthAccessNodes;
46-
const set = arrayExpression.elements[0].argument;
68+
} = lengthAccessNode;
4769

48-
if (sourceCode.getCommentsInside(arrayExpression).length > sourceCode.getCommentsInside(set).length) {
70+
if (sourceCode.getCommentsInside(array).length > sourceCode.getCommentsInside(set).length) {
4971
return;
5072
}
5173

5274
/** @param {import('eslint').Rule.RuleFixer} fixer */
5375
return function * (fixer) {
5476
yield fixer.replaceText(property, 'size');
55-
yield fixer.replaceText(arrayExpression, sourceCode.getText(set));
56-
yield fixSpaceAroundKeyword(fixer, lengthAccessNodes, context);
77+
yield fixer.replaceText(array, sourceCode.getText(set));
78+
79+
if (array.type === 'ArrayExpression') {
80+
yield fixSpaceAroundKeyword(fixer, lengthAccessNode, context);
81+
}
5782
};
5883
}
5984

@@ -67,22 +92,19 @@ const create = context => {
6792
property: 'length',
6893
optional: false,
6994
})
70-
|| node.object.type !== 'ArrayExpression'
71-
|| node.object.elements.length !== 1
72-
|| node.object.elements[0]?.type !== 'SpreadElement'
7395
) {
7496
return;
7597
}
7698

77-
const maybeSet = node.object.elements[0].argument;
78-
if (!isSet(maybeSet, sourceCode.getScope(maybeSet))) {
99+
const set = getSetNode(node.object);
100+
if (!set || !isSet(set, sourceCode.getScope(set))) {
79101
return;
80102
}
81103

82104
return {
83105
node: node.property,
84106
messageId: MESSAGE_ID,
85-
fix: fix(context, node),
107+
fix: createFix(context, node, set),
86108
};
87109
});
88110
};

test/prefer-set-size.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,23 @@ test.snapshot({
2222
'[...foo].length',
2323
'var foo = new Set(); var foo = new Set(); [...foo].length',
2424
'[,].length',
25+
// `Array.from` — valid cases
26+
'Array.from(foo).length',
27+
'Array.from(new NotSet(array)).length',
28+
'Array.from(Set(array)).length',
29+
'Array.from(new Set(array)).notLength',
30+
'Array.from(new Set(array))?.length',
31+
'Array.from(new Set(array))[length]',
32+
'Array.from(new Set(array))["length"]',
33+
'Array.from(new Set(array), mapFn).length',
34+
'Array?.from(new Set(array)).length',
35+
'Array.from?.(new Set(array)).length',
36+
'const foo = new NotSet([]);Array.from(foo).length;',
37+
'let foo = new Set([]);Array.from(foo).length;',
38+
'const {foo} = new Set([]);Array.from(foo).length;',
39+
'const [foo] = new Set([]);Array.from(foo).length;',
40+
'var foo = new Set(); var foo = new Set(); Array.from(foo).length',
41+
'NotArray.from(new Set(array)).length',
2542
],
2643
invalid: [
2744
'[...new Set(array)].length',
@@ -43,5 +60,20 @@ test.snapshot({
4360
`,
4461
'[/* comment */...new Set(array)].length',
4562
'[...new /* comment */ Set(array)].length',
63+
// `Array.from` — invalid cases
64+
'Array.from(new Set(array)).length',
65+
outdent`
66+
const foo = new Set([]);
67+
console.log(Array.from(foo).length);
68+
`,
69+
'Array.from((( new Set(array) ))).length',
70+
'(( Array.from(new Set(array)) )).length',
71+
'Array.from(/* comment */ new Set(array)).length',
72+
'Array.from(new /* comment */ Set(array)).length',
73+
outdent`
74+
function isUnique(array) {
75+
return Array.from(new Set(array)).length === array.length
76+
}
77+
`,
4678
],
4779
});

test/snapshots/prefer-set-size.js.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,142 @@ Generated by [AVA](https://avajs.dev).
183183
Output:␊
184184
1 | new /* comment */ Set(array).size␊
185185
`
186+
187+
## invalid(10): Array.from(new Set(array)).length
188+
189+
> Input
190+
191+
`␊
192+
1 | Array.from(new Set(array)).length␊
193+
`
194+
195+
> Error 1/1
196+
197+
`␊
198+
Message:␊
199+
> 1 | Array.from(new Set(array)).length␊
200+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
201+
202+
Output:␊
203+
1 | new Set(array).size␊
204+
`
205+
206+
## invalid(11): const foo = new Set([]); console.log(Array.from(foo).length);
207+
208+
> Input
209+
210+
`␊
211+
1 | const foo = new Set([]);␊
212+
2 | console.log(Array.from(foo).length);␊
213+
`
214+
215+
> Error 1/1
216+
217+
`␊
218+
Message:␊
219+
1 | const foo = new Set([]);␊
220+
> 2 | console.log(Array.from(foo).length);␊
221+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
222+
223+
Output:␊
224+
1 | const foo = new Set([]);␊
225+
2 | console.log(foo.size);␊
226+
`
227+
228+
## invalid(12): Array.from((( new Set(array) ))).length
229+
230+
> Input
231+
232+
`␊
233+
1 | Array.from((( new Set(array) ))).length␊
234+
`
235+
236+
> Error 1/1
237+
238+
`␊
239+
Message:␊
240+
> 1 | Array.from((( new Set(array) ))).length␊
241+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
242+
243+
Output:␊
244+
1 | new Set(array).size␊
245+
`
246+
247+
## invalid(13): (( Array.from(new Set(array)) )).length
248+
249+
> Input
250+
251+
`␊
252+
1 | (( Array.from(new Set(array)) )).length␊
253+
`
254+
255+
> Error 1/1
256+
257+
`␊
258+
Message:␊
259+
> 1 | (( Array.from(new Set(array)) )).length␊
260+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
261+
262+
Output:␊
263+
1 | (( new Set(array) )).size␊
264+
`
265+
266+
## invalid(14): Array.from(/* comment */ new Set(array)).length
267+
268+
> Input
269+
270+
`␊
271+
1 | Array.from(/* comment */ new Set(array)).length␊
272+
`
273+
274+
> Error 1/1
275+
276+
`␊
277+
Message:␊
278+
> 1 | Array.from(/* comment */ new Set(array)).length␊
279+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
280+
`
281+
282+
## invalid(15): Array.from(new /* comment */ Set(array)).length
283+
284+
> Input
285+
286+
`␊
287+
1 | Array.from(new /* comment */ Set(array)).length␊
288+
`
289+
290+
> Error 1/1
291+
292+
`␊
293+
Message:␊
294+
> 1 | Array.from(new /* comment */ Set(array)).length␊
295+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
296+
297+
Output:␊
298+
1 | new /* comment */ Set(array).size␊
299+
`
300+
301+
## invalid(16): function isUnique(array) { return Array.from(new Set(array)).length === array.length }
302+
303+
> Input
304+
305+
`␊
306+
1 | function isUnique(array) {␊
307+
2 | return Array.from(new Set(array)).length === array.length␊
308+
3 | }␊
309+
`
310+
311+
> Error 1/1
312+
313+
`␊
314+
Message:␊
315+
1 | function isUnique(array) {␊
316+
> 2 | return Array.from(new Set(array)).length === array.length␊
317+
| ^^^^^^ Prefer using \`Set#size\` instead of \`Array#length\`.␊
318+
3 | }␊
319+
320+
Output:␊
321+
1 | function isUnique(array) {␊
322+
2 | return new Set(array).size === array.length␊
323+
3 | }␊
324+
`
271 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)