UNPKG

7.65 kBJavaScriptView Raw
1"use strict";
2var __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}));
9var __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});
14var __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};
21Object.defineProperty(exports, "__esModule", { value: true });
22const utils_1 = require("@typescript-eslint/utils");
23const util = __importStar(require("../util"));
24exports.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 // x++ or ++x
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 // x += 1
77 return isLiteral(node.right, 1);
78 }
79 else if (node.operator === '=') {
80 // x = x + 1 or x = 1 + x
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 // a[i] = 1, a[i] += 1, etc.
103 if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
104 parent.left === node) {
105 return true;
106 }
107 // delete a[i]
108 if (parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
109 parent.operator === 'delete' &&
110 parent.argument === node) {
111 return true;
112 }
113 // a[i]++, --a[i], etc.
114 if (parent.type === utils_1.AST_NODE_TYPES.UpdateExpression &&
115 parent.argument === node) {
116 return true;
117 }
118 // [a[i]] = [0]
119 if (parent.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
120 return true;
121 }
122 // [...a[i]] = [0]
123 if (parent.type === utils_1.AST_NODE_TYPES.RestElement) {
124 return true;
125 }
126 // ({ foo: a[i] }) = { foo: 0 }
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//# sourceMappingURL=prefer-for-of.js.map
\No newline at end of file