Skip to content

Commit

Permalink
prefer-flat-map: Report for common concat pattern (#323)
Browse files Browse the repository at this point in the history
  • Loading branch information
MrHen authored and sindresorhus committed Jun 24, 2019
1 parent 0fb6fcf commit c2b8e76
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 26 deletions.
1 change: 1 addition & 0 deletions docs/rules/prefer-flat-map.md
Expand Up @@ -9,6 +9,7 @@ This rule is fixable.

```js
[1, 2, 3].map(i => [i]).flat();
[].concat(...foo.map((i) => i))
```


Expand Down
41 changes: 36 additions & 5 deletions rules/prefer-flat-map.js
Expand Up @@ -2,9 +2,33 @@
const getDocsUrl = require('./utils/get-docs-url');
const isMethodNamed = require('./utils/is-method-named');

const MESSAGE_ID = 'preferFlatMap';
const MESSAGE_ID_FLATMAP = 'flat-map';
const MESSAGE_ID_SPREAD = 'spread';

const report = (context, nodeFlat, nodeMap) => {
const SELECTOR_SPREAD = [
// [].concat(...bar.map((i) => i))
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'CallExpression',

// [].concat(...bar.map((i) => i))
// ^^
'[callee.object.type="ArrayExpression"]',
'[callee.object.elements.length=0]',

// [].concat(...bar.map((i) => i))
// ^^^^^^
'[callee.property.name="concat"]',

// [].concat(...bar.map((i) => i))
// ^^^^^^^^^^^^^^^^^^^^
'[arguments.0.type="SpreadElement"]',

// [].concat(...bar.map((i) => i))
// ^^^
'[arguments.0.argument.callee.property.name="map"]'
].join('');

const reportFlatMap = (context, nodeFlat, nodeMap) => {
const source = context.getSourceCode();

// Node covers:
Expand Down Expand Up @@ -59,7 +83,7 @@ const report = (context, nodeFlat, nodeMap) => {

context.report({
node: nodeFlat,
messageId: MESSAGE_ID,
messageId: MESSAGE_ID_FLATMAP,
fix: fixer => {
const fixings = [
// Removes:
Expand Down Expand Up @@ -112,7 +136,13 @@ const create = context => ({
return;
}

report(context, node, parent);
reportFlatMap(context, node, parent);
},
[SELECTOR_SPREAD]: node => {
context.report({
node,
messageId: MESSAGE_ID_SPREAD
});
}
});

Expand All @@ -125,7 +155,8 @@ module.exports = {
},
fixable: 'code',
messages: {
[MESSAGE_ID]: 'Prefer `.flatMap(…)` over `.map(…).flat()`.'
[MESSAGE_ID_FLATMAP]: 'Prefer `.flatMap(…)` over `.map(…).flat()`.',
[MESSAGE_ID_SPREAD]: 'Prefer `.flatMap(…)` over `[].concat(...foo.map(…))`.'
}
}
};
59 changes: 38 additions & 21 deletions test/prefer-flat-map.js
Expand Up @@ -9,9 +9,14 @@ const ruleTester = avaRuleTester(test, {
}
});

const error = {
const errorFlatMap = {
ruleId: 'prefer-flat-map',
messageId: 'preferFlatMap'
messageId: 'flat-map'
};

const errorSpread = {
ruleId: 'prefer-flat-map',
messageId: 'spread'
};

ruleTester.run('prefer-flat-map', rule, {
Expand All @@ -28,53 +33,55 @@ ruleTester.run('prefer-flat-map', rule, {
bar = bar.flat();
`,
'const bar = [[1],[2],[3]].map(i => [i]).flat(2)',
'const bar = [[1],[2],[3]].map(i => [i]).flat(1, null)'
'const bar = [[1],[2],[3]].map(i => [i]).flat(1, null)',
'const foo = [1,2,3].concat(...[4,5,6].map((i) => i));',
'const foo = [].concat(...[[4,5],6].flat());'
],
invalid: [
{
code: 'const bar = [1,2,3].map(i => [i]).flat()',
output: 'const bar = [1,2,3].flatMap(i => [i])',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = [1,2,3].map((i) => [i]).flat()',
output: 'const bar = [1,2,3].flatMap((i) => [i])',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = [1,2,3].map((i) => { return [i]; }).flat()',
output: 'const bar = [1,2,3].flatMap((i) => { return [i]; })',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = [1,2,3].map(foo).flat()',
output: 'const bar = [1,2,3].flatMap(foo)',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = foo.map(i => [i]).flat()',
output: 'const bar = foo.flatMap(i => [i])',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = { map: () => {} }.map(i => [i]).flat()',
output: 'const bar = { map: () => {} }.flatMap(i => [i])',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = [1,2,3].map(i => i).map(i => [i]).flat()',
output: 'const bar = [1,2,3].map(i => i).flatMap(i => [i])',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = [1,2,3].sort().map(i => [i]).flat()',
output: 'const bar = [1,2,3].sort().flatMap(i => [i])',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = (([1,2,3].map(i => [i]))).flat()',
output: 'const bar = (([1,2,3].flatMap(i => [i])))',
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -87,7 +94,7 @@ ruleTester.run('prefer-flat-map', rule, {
return [i];
});
`,
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -101,7 +108,7 @@ ruleTester.run('prefer-flat-map', rule, {
return [i];
});
` + '\n',
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -115,7 +122,7 @@ ruleTester.run('prefer-flat-map', rule, {
return [i];
}); // comment
` + '\n',
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -130,7 +137,7 @@ ruleTester.run('prefer-flat-map', rule, {
}); // comment
// other
`,
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -142,7 +149,7 @@ ruleTester.run('prefer-flat-map', rule, {
let bar = [1,2,3]
.flatMap(i => { return [i]; });
` + '\n\t',
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -152,17 +159,17 @@ ruleTester.run('prefer-flat-map', rule, {
output: outdent`
let bar = [1,2,3].flatMap(i => { return [i]; });
` + '\n\t',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'let bar = [1,2,3] . map( x => y ) . flat () // 🤪',
output: 'let bar = [1,2,3] . flatMap( x => y ) // 🤪',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const bar = [1,2,3].map(i => [i]).flat(1);',
output: 'const bar = [1,2,3].flatMap(i => [i]);',
errors: [error]
errors: [errorFlatMap]
},
{
code: outdent`
Expand All @@ -176,7 +183,17 @@ ruleTester.run('prefer-flat-map', rule, {
.filter(foo => !!foo.zaz)
.flatMap(foo => doFoo(foo));
` + '\n\t',
errors: [error]
errors: [errorFlatMap]
},
{
code: 'const foo = [].concat(...bar.map((i) => i));',
output: 'const foo = [].concat(...bar.map((i) => i));',
errors: [errorSpread]
},
{
code: 'const foo = [].concat(...[1,2,3].map((i) => i));',
output: 'const foo = [].concat(...[1,2,3].map((i) => i));',
errors: [errorSpread]
}
]
});

0 comments on commit c2b8e76

Please sign in to comment.