summaryrefslogtreecommitdiff
path: root/tools/eslint-rules/no-array-destructuring.js
blob: b65849aa95f925a3589830f08660452521c7a8c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
 * @fileoverview Iterating over arrays should be avoided because it relies on
 *               user-mutable global methods (`Array.prototype[Symbol.iterator]`
 *               and `%ArrayIteratorPrototype%.next`), we should instead use
 *               other alternatives. This file defines a rule that disallow
 *               array destructuring syntax in favor of object destructuring
 *               syntax. Note that you can ignore this rule if you are using
 *               the array destructuring syntax over a safe iterable, or
 *               actually want to iterate over a user-provided object.
 * @author aduh95 <duhamelantoine1995@gmail.com>
 */
'use strict';

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

const USE_OBJ_DESTRUCTURING =
  'Use object destructuring instead of array destructuring.';
const USE_ARRAY_METHODS =
  'Use primordials.ArrayPrototypeSlice to avoid unsafe array iteration.';

const findComma = (sourceCode, elements, i, start) => {
  if (i === 0)
    return sourceCode.getTokenAfter(sourceCode.getTokenByRangeStart(start));

  let element;
  const originalIndex = i;
  while (i && !element) {
    element = elements[--i];
  }
  let token = sourceCode.getTokenAfter(
    element ?? sourceCode.getTokenByRangeStart(start),
  );
  for (; i < originalIndex; i++) {
    token = sourceCode.getTokenAfter(token);
  }
  return token;
};
const createFix = (fixer, sourceCode, { range: [start, end], elements }) => [
  fixer.replaceTextRange([start, start + 1], '{'),
  fixer.replaceTextRange([end - 1, end], '}'),
  ...elements.map((node, i) =>
    (node === null ?
      fixer.remove(findComma(sourceCode, elements, i, start)) :
      fixer.insertTextBefore(node, i + ':')),
  ),
];
const arrayPatternContainsRestOperator = ({ elements }) =>
  elements?.find((node) => node?.type === 'RestElement');

module.exports = {
  meta: {
    type: 'suggestion',
    fixable: 'code',
    schema: [],
  },
  create(context) {
    const sourceCode = context.getSourceCode();

    return {
      ArrayPattern(node) {
        const hasRest = arrayPatternContainsRestOperator(node);
        context.report({
          message: hasRest ? USE_ARRAY_METHODS : USE_OBJ_DESTRUCTURING,
          node: hasRest || node,
          fix: hasRest ?
            undefined :
            (fixer) => [
              ...(node.parent.type === 'AssignmentExpression' ?
                [
                  fixer.insertTextBefore(node.parent, '('),
                  fixer.insertTextAfter(node.parent, ')'),
                ] :
                []),
              ...createFix(fixer, sourceCode, node),
            ],
        });

      },
    };
  },
};