1 | "use strict";
|
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3 | if (k2 === undefined) k2 = k;
|
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5 | }) : (function(o, m, k, k2) {
|
6 | if (k2 === undefined) k2 = k;
|
7 | o[k2] = m[k];
|
8 | }));
|
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11 | }) : function(o, v) {
|
12 | o["default"] = v;
|
13 | });
|
14 | var __importStar = (this && this.__importStar) || function (mod) {
|
15 | if (mod && mod.__esModule) return mod;
|
16 | var result = {};
|
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18 | __setModuleDefault(result, mod);
|
19 | return result;
|
20 | };
|
21 | Object.defineProperty(exports, "__esModule", { value: true });
|
22 | const utils_1 = require("@typescript-eslint/utils");
|
23 | const util = __importStar(require("../util"));
|
24 | exports.default = util.createRule({
|
25 | name: 'prefer-for-of',
|
26 | meta: {
|
27 | type: 'suggestion',
|
28 | docs: {
|
29 | description: 'Prefer a ‘for-of’ loop over a standard ‘for’ loop if the index is only used to access the array being iterated',
|
30 | recommended: false,
|
31 | },
|
32 | messages: {
|
33 | preferForOf: 'Expected a `for-of` loop instead of a `for` loop with this simple iteration.',
|
34 | },
|
35 | schema: [],
|
36 | },
|
37 | defaultOptions: [],
|
38 | create(context) {
|
39 | function isSingleVariableDeclaration(node) {
|
40 | return (node !== null &&
|
41 | node.type === utils_1.AST_NODE_TYPES.VariableDeclaration &&
|
42 | node.kind !== 'const' &&
|
43 | node.declarations.length === 1);
|
44 | }
|
45 | function isLiteral(node, value) {
|
46 | return node.type === utils_1.AST_NODE_TYPES.Literal && node.value === value;
|
47 | }
|
48 | function isZeroInitialized(node) {
|
49 | return node.init !== null && isLiteral(node.init, 0);
|
50 | }
|
51 | function isMatchingIdentifier(node, name) {
|
52 | return node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === name;
|
53 | }
|
54 | function isLessThanLengthExpression(node, name) {
|
55 | if (node !== null &&
|
56 | node.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
57 | node.operator === '<' &&
|
58 | isMatchingIdentifier(node.left, name) &&
|
59 | node.right.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
60 | isMatchingIdentifier(node.right.property, 'length')) {
|
61 | return node.right.object;
|
62 | }
|
63 | return null;
|
64 | }
|
65 | function isIncrement(node, name) {
|
66 | if (!node) {
|
67 | return false;
|
68 | }
|
69 | switch (node.type) {
|
70 | case utils_1.AST_NODE_TYPES.UpdateExpression:
|
71 |
|
72 | return (node.operator === '++' && isMatchingIdentifier(node.argument, name));
|
73 | case utils_1.AST_NODE_TYPES.AssignmentExpression:
|
74 | if (isMatchingIdentifier(node.left, name)) {
|
75 | if (node.operator === '+=') {
|
76 |
|
77 | return isLiteral(node.right, 1);
|
78 | }
|
79 | else if (node.operator === '=') {
|
80 |
|
81 | const expr = node.right;
|
82 | return (expr.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
83 | expr.operator === '+' &&
|
84 | ((isMatchingIdentifier(expr.left, name) &&
|
85 | isLiteral(expr.right, 1)) ||
|
86 | (isLiteral(expr.left, 1) &&
|
87 | isMatchingIdentifier(expr.right, name))));
|
88 | }
|
89 | }
|
90 | }
|
91 | return false;
|
92 | }
|
93 | function contains(outer, inner) {
|
94 | return (outer.range[0] <= inner.range[0] && outer.range[1] >= inner.range[1]);
|
95 | }
|
96 | function isAssignee(node) {
|
97 | var _a;
|
98 | const parent = node.parent;
|
99 | if (!parent) {
|
100 | return false;
|
101 | }
|
102 |
|
103 | if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
104 | parent.left === node) {
|
105 | return true;
|
106 | }
|
107 |
|
108 | if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
109 | parent.operator === 'delete' &&
|
110 | parent.argument === node) {
|
111 | return true;
|
112 | }
|
113 |
|
114 | if (parent.type === utils_1.AST_NODE_TYPES.UpdateExpression &&
|
115 | parent.argument === node) {
|
116 | return true;
|
117 | }
|
118 |
|
119 | if (parent.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
|
120 | return true;
|
121 | }
|
122 |
|
123 | if (parent.type === utils_1.AST_NODE_TYPES.RestElement) {
|
124 | return true;
|
125 | }
|
126 |
|
127 | if (parent.type === utils_1.AST_NODE_TYPES.Property &&
|
128 | parent.value === node &&
|
129 | ((_a = parent.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
130 | isAssignee(parent.parent)) {
|
131 | return true;
|
132 | }
|
133 | return false;
|
134 | }
|
135 | function isIndexOnlyUsedWithArray(body, indexVar, arrayExpression) {
|
136 | const sourceCode = context.getSourceCode();
|
137 | const arrayText = sourceCode.getText(arrayExpression);
|
138 | return indexVar.references.every(reference => {
|
139 | const id = reference.identifier;
|
140 | const node = id.parent;
|
141 | return (!contains(body, id) ||
|
142 | (node !== undefined &&
|
143 | node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
144 | node.object.type !== utils_1.AST_NODE_TYPES.ThisExpression &&
|
145 | node.property === id &&
|
146 | sourceCode.getText(node.object) === arrayText &&
|
147 | !isAssignee(node)));
|
148 | });
|
149 | }
|
150 | return {
|
151 | 'ForStatement:exit'(node) {
|
152 | if (!isSingleVariableDeclaration(node.init)) {
|
153 | return;
|
154 | }
|
155 | const declarator = node.init.declarations[0];
|
156 | if (!declarator ||
|
157 | !isZeroInitialized(declarator) ||
|
158 | declarator.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
159 | return;
|
160 | }
|
161 | const indexName = declarator.id.name;
|
162 | const arrayExpression = isLessThanLengthExpression(node.test, indexName);
|
163 | if (!arrayExpression) {
|
164 | return;
|
165 | }
|
166 | const [indexVar] = context.getDeclaredVariables(node.init);
|
167 | if (isIncrement(node.update, indexName) &&
|
168 | isIndexOnlyUsedWithArray(node.body, indexVar, arrayExpression)) {
|
169 | context.report({
|
170 | node,
|
171 | messageId: 'preferForOf',
|
172 | });
|
173 | }
|
174 | },
|
175 | };
|
176 | },
|
177 | });
|
178 |
|
\ | No newline at end of file |