no-array-reduce.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. 'use strict';
  2. const {get} = require('lodash');
  3. const {methodCallSelector} = require('./selectors/index.js');
  4. const {arrayPrototypeMethodSelector, notFunctionSelector, matches} = require('./selectors/index.js');
  5. const MESSAGE_ID = 'no-reduce';
  6. const messages = {
  7. [MESSAGE_ID]: '`Array#{{method}}()` is not allowed',
  8. };
  9. const prototypeSelector = method => [
  10. methodCallSelector(method),
  11. arrayPrototypeMethodSelector({
  12. path: 'callee.object',
  13. methods: ['reduce', 'reduceRight'],
  14. }),
  15. ].join('');
  16. const selector = matches([
  17. // `array.{reduce,reduceRight}()`
  18. [
  19. methodCallSelector({methods: ['reduce', 'reduceRight'], minimumArguments: 1, maximumArguments: 2}),
  20. notFunctionSelector('arguments.0'),
  21. ' > .callee > .property',
  22. ].join(''),
  23. // `[].{reduce,reduceRight}.call()` and `Array.{reduce,reduceRight}.call()`
  24. [
  25. prototypeSelector('call'),
  26. notFunctionSelector('arguments.1'),
  27. ' > .callee > .object > .property',
  28. ].join(''),
  29. // `[].{reduce,reduceRight}.apply()` and `Array.{reduce,reduceRight}.apply()`
  30. [
  31. prototypeSelector('apply'),
  32. ' > .callee > .object > .property',
  33. ].join(''),
  34. ]);
  35. const schema = [
  36. {
  37. type: 'object',
  38. additionalProperties: false,
  39. properties: {
  40. allowSimpleOperations: {
  41. type: 'boolean',
  42. default: true,
  43. },
  44. },
  45. },
  46. ];
  47. /** @param {import('eslint').Rule.RuleContext} context */
  48. const create = context => {
  49. const {allowSimpleOperations} = {allowSimpleOperations: true, ...context.options[0]};
  50. return {
  51. [selector](node) {
  52. const callback = get(node, 'parent.parent.arguments[0]', {});
  53. const problem = {
  54. node,
  55. messageId: MESSAGE_ID,
  56. data: {method: node.name},
  57. };
  58. if (!allowSimpleOperations) {
  59. return problem;
  60. }
  61. if (callback.type === 'ArrowFunctionExpression' && callback.body.type === 'BinaryExpression') {
  62. return;
  63. }
  64. if ((callback.type === 'ArrowFunctionExpression' || callback.type === 'FunctionExpression')
  65. && callback.body.type === 'BlockStatement'
  66. && callback.body.body.length === 1
  67. && callback.body.body[0].type === 'ReturnStatement'
  68. && callback.body.body[0].argument.type === 'BinaryExpression') {
  69. return;
  70. }
  71. return problem;
  72. },
  73. };
  74. };
  75. /** @type {import('eslint').Rule.RuleModule} */
  76. module.exports = {
  77. create,
  78. meta: {
  79. type: 'suggestion',
  80. docs: {
  81. description: 'Disallow `Array#reduce()` and `Array#reduceRight()`.',
  82. },
  83. schema,
  84. messages,
  85. },
  86. };