Skip to content

Commit 30a3dbf

Browse files
authored
Merge branch 'main' into boolean-update
2 parents 2964450 + bc908fd commit 30a3dbf

File tree

6 files changed

+113
-3
lines changed

6 files changed

+113
-3
lines changed

eslint-plugin-expensify/CONST.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@ module.exports = {
2929
NO_DEFAULT_PROPS: 'defaultProps should not be used in function components. Use default Arguments instead.',
3030
USE_PERIODS_ERROR_MESSAGES: 'Use periods at the end of error messages.',
3131
USE_DOUBLE_NEGATION_INSTEAD_OF_BOOLEAN: 'Use !! instead of Boolean().',
32+
NO_ACC_SPREAD_IN_REDUCE: 'Avoid a use of spread (`...`) operator on accumulators in reduce callback. Mutate them directly instead.',
3233
},
3334
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const _ = require('lodash');
2+
const CONST = require('./CONST');
3+
4+
// Matches function expression as a direct descendant (argument callback) of "reduce" or "reduceRight" call
5+
const MATCH = 'CallExpression:matches([callee.property.name="reduce"], [callee.property.name="reduceRight"]) > :matches(ArrowFunctionExpression, FunctionExpression)';
6+
7+
module.exports = {
8+
meta: {
9+
type: 'problem',
10+
docs: {
11+
description: CONST.MESSAGE.NO_ACC_SPREAD_IN_REDUCE,
12+
category: 'Best Practices',
13+
recommended: false,
14+
},
15+
schema: [], // no options
16+
},
17+
create(context) {
18+
return {
19+
[MATCH](node) {
20+
// Retrieve accumulator variable
21+
const accumulator = context.getDeclaredVariables(node)[0];
22+
if (!accumulator) {
23+
return;
24+
}
25+
26+
// Check if accumulatorVariable has any references (is used in the scope)
27+
if (!accumulator.references.length) {
28+
return;
29+
}
30+
31+
// Check if any of the references are used in a SpreadElement
32+
const isAccumulatorVariableUsedSpread = _.some(
33+
accumulator.references,
34+
reference => reference.identifier.parent.type === 'SpreadElement',
35+
);
36+
if (!isAccumulatorVariableUsedSpread) {
37+
return;
38+
}
39+
40+
// Accumulator variable is used in a SpreadElement, report it
41+
context.report({
42+
node,
43+
message: CONST.MESSAGE.NO_ACC_SPREAD_IN_REDUCE,
44+
});
45+
},
46+
};
47+
},
48+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const RuleTester = require('eslint').RuleTester;
2+
const rule = require('../no-acc-spread-in-reduce');
3+
const CONST = require('../CONST');
4+
5+
const ruleTester = new RuleTester({
6+
parserOptions: {
7+
ecmaVersion: 2018,
8+
sourceType: 'module',
9+
},
10+
});
11+
12+
const errors = [
13+
{
14+
message: CONST.MESSAGE.NO_ACC_SPREAD_IN_REDUCE,
15+
},
16+
];
17+
18+
ruleTester.run('no-spread-in-reduce', rule, {
19+
valid: [
20+
{
21+
code: `
22+
array.reduce((acc, item) => {
23+
acc[item.key] = item.value;
24+
return acc;
25+
}
26+
, {});
27+
`,
28+
},
29+
{
30+
code: `
31+
array.reduce((acc, item) => {
32+
const spread = { ...somethingElse };
33+
return acc[item.key] = item.value;
34+
}, {});
35+
`,
36+
},
37+
],
38+
invalid: [
39+
{
40+
code: `
41+
const arr = [];
42+
arr.reduce((acc, i) => ({ ...acc, i }), {})
43+
`,
44+
errors,
45+
},
46+
{
47+
code: `
48+
const arr = [];
49+
arr.reduceRight((acc, i) => ({ ...acc, i }), {})
50+
`,
51+
errors,
52+
},
53+
{
54+
code: `
55+
([]).reduce((acc, i) => ({ ...acc, i }), {})
56+
`,
57+
errors,
58+
},
59+
],
60+
});

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "eslint-config-expensify",
3-
"version": "2.0.48",
3+
"version": "2.0.49",
44
"description": "Expensify's ESLint configuration following our style guide",
55
"main": "index.js",
66
"repository": {

rules/expensify.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
'rulesdir/no-api-side-effects-method': 'error',
1717
'rulesdir/prefer-localization': 'error',
1818
'rulesdir/use-double-negation-instead-of-boolean': 'error',
19+
'rulesdir/no-acc-spread-in-reduce': 'error',
1920
'no-restricted-imports': ['error', {
2021
paths: [{
2122
name: 'react-native',

0 commit comments

Comments
 (0)