@blumintinc/eslint-plugin-blumint 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -5
- package/lib/index.js +7 -1
- package/lib/rules/consistent-callback-naming.js +26 -0
- package/lib/rules/dynamic-https-errors.js +5 -26
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +1 -0
- package/lib/rules/enforce-boolean-naming-prefixes.js +86 -119
- package/lib/rules/enforce-dynamic-imports.d.ts +2 -1
- package/lib/rules/enforce-dynamic-imports.js +42 -21
- package/lib/rules/enforce-memoize-async.js +1 -4
- package/lib/rules/enforce-mui-rounded-icons.js +42 -1
- package/lib/rules/enforce-verb-noun-naming.js +3 -0
- package/lib/rules/global-const-style.js +9 -0
- package/lib/rules/logical-top-to-bottom-grouping.js +80 -2
- package/lib/rules/memo-compare-deeply-complex-props.js +167 -3
- package/lib/rules/memo-nested-react-components.js +143 -8
- package/lib/rules/no-array-length-in-deps.js +74 -3
- package/lib/rules/no-circular-references.js +145 -482
- package/lib/rules/no-compositing-layer-props.js +31 -0
- package/lib/rules/no-entire-object-hook-deps.js +132 -97
- package/lib/rules/no-explicit-return-type.js +6 -0
- package/lib/rules/no-hungarian.js +119 -24
- package/lib/rules/no-margin-properties.js +7 -38
- package/lib/rules/no-unnecessary-verb-suffix.js +79 -0
- package/lib/rules/no-unused-props.js +215 -37
- package/lib/rules/no-useless-fragment.js +10 -2
- package/lib/rules/parallelize-async-operations.js +1 -3
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +73 -11
- package/lib/rules/prefer-use-deep-compare-memo.js +8 -14
- package/lib/rules/react-memoize-literals.js +87 -1
- package/lib/rules/require-migration-script-metadata.d.ts +9 -0
- package/lib/rules/require-migration-script-metadata.js +206 -0
- package/lib/rules/warn-https-error-message-user-friendly.d.ts +1 -0
- package/lib/rules/warn-https-error-message-user-friendly.js +239 -0
- package/lib/utils/ASTHelpers.d.ts +15 -0
- package/lib/utils/ASTHelpers.js +48 -0
- package/package.json +7 -6
- package/release-manifest.json +166 -0
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noCircularReferences = void 0;
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const ASTHelpers_1 = require("../utils/ASTHelpers");
|
|
5
6
|
const createRule_1 = require("../utils/createRule");
|
|
6
|
-
const objectMap = new WeakMap();
|
|
7
|
-
const scopeMap = new Map();
|
|
8
|
-
const circularRefs = new WeakSet();
|
|
9
7
|
exports.noCircularReferences = (0, createRule_1.createRule)({
|
|
10
8
|
name: 'no-circular-references',
|
|
11
9
|
meta: {
|
|
@@ -22,6 +20,7 @@ exports.noCircularReferences = (0, createRule_1.createRule)({
|
|
|
22
20
|
defaultOptions: [],
|
|
23
21
|
create(context) {
|
|
24
22
|
const sourceCode = context.getSourceCode();
|
|
23
|
+
const objectMap = new WeakMap();
|
|
25
24
|
function reportCircularReference(node, reference) {
|
|
26
25
|
context.report({
|
|
27
26
|
node,
|
|
@@ -31,157 +30,114 @@ exports.noCircularReferences = (0, createRule_1.createRule)({
|
|
|
31
30
|
},
|
|
32
31
|
});
|
|
33
32
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
33
|
+
function getUnwrappedObjectOrArray(node) {
|
|
34
|
+
const current = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(node);
|
|
35
|
+
return current.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
|
|
36
|
+
current.type === utils_1.AST_NODE_TYPES.ArrayExpression
|
|
37
|
+
? current
|
|
38
|
+
: null;
|
|
36
39
|
}
|
|
37
40
|
function isIdentifier(node) {
|
|
38
41
|
return node.type === utils_1.AST_NODE_TYPES.Identifier;
|
|
39
42
|
}
|
|
40
|
-
function isThisExpression(node) {
|
|
41
|
-
return node.type === utils_1.AST_NODE_TYPES.ThisExpression;
|
|
42
|
-
}
|
|
43
|
-
function getScopeId(scope) {
|
|
44
|
-
return `${scope.type}:${scope.block.range[0]}:${scope.block.range[1]}`;
|
|
45
|
-
}
|
|
46
43
|
function isFunction(node) {
|
|
47
44
|
return (node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
48
45
|
node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
49
46
|
node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration);
|
|
50
47
|
}
|
|
51
|
-
function isArray(node) {
|
|
52
|
-
return (node.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
53
|
-
node.type === utils_1.AST_NODE_TYPES.ArrayPattern);
|
|
54
|
-
}
|
|
55
|
-
function isClass(node) {
|
|
56
|
-
return (node.type === utils_1.AST_NODE_TYPES.ClassExpression ||
|
|
57
|
-
node.type === utils_1.AST_NODE_TYPES.ClassDeclaration ||
|
|
58
|
-
node.type === utils_1.AST_NODE_TYPES.NewExpression);
|
|
59
|
-
}
|
|
60
|
-
function isPromise(node) {
|
|
61
|
-
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
62
|
-
const callee = node.callee;
|
|
63
|
-
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
64
|
-
return (callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
65
|
-
callee.object.name === 'Promise' &&
|
|
66
|
-
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
67
|
-
callee.property.name === 'resolve');
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
48
|
function isPrimitive(node) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
node.callee.name === 'fn') ||
|
|
82
|
-
(node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
83
|
-
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
84
|
-
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
85
|
-
(node.property.name === 'a' ||
|
|
86
|
-
node.property.name === 'b' ||
|
|
87
|
-
node.property.name === 'c' ||
|
|
88
|
-
node.property.name === 'd')) ||
|
|
89
|
-
(node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
90
|
-
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
91
|
-
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
92
|
-
node.property.name === 'func') ||
|
|
93
|
-
(node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
94
|
-
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
95
|
-
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
96
|
-
node.property.name === 'self') ||
|
|
97
|
-
(node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
98
|
-
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
99
|
-
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
100
|
-
node.property.name === 'promise') ||
|
|
101
|
-
(node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
102
|
-
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
103
|
-
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
104
|
-
node.property.name === 'ref') ||
|
|
105
|
-
(node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
106
|
-
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
107
|
-
node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
108
|
-
node.property.name === 'method'));
|
|
49
|
+
// Unwrap TS assertions (e.g. `undefined as unknown`) before checking primitiveness.
|
|
50
|
+
const unwrapped = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(node);
|
|
51
|
+
if (unwrapped.type === utils_1.AST_NODE_TYPES.Literal)
|
|
52
|
+
return true;
|
|
53
|
+
if (isIdentifier(unwrapped) &&
|
|
54
|
+
(unwrapped.name === 'undefined' || unwrapped.name === 'null'))
|
|
55
|
+
return true;
|
|
56
|
+
return false;
|
|
109
57
|
}
|
|
110
|
-
function getVariable(
|
|
111
|
-
let scope =
|
|
58
|
+
function getVariable(node) {
|
|
59
|
+
let scope = ASTHelpers_1.ASTHelpers.getScope(context, node);
|
|
112
60
|
while (scope) {
|
|
113
|
-
const variable = scope.variables.find((v) => v.name === name);
|
|
114
|
-
if (variable)
|
|
61
|
+
const variable = scope.variables.find((v) => v.name === node.name);
|
|
62
|
+
if (variable)
|
|
115
63
|
return variable;
|
|
116
|
-
}
|
|
117
64
|
scope = scope.upper;
|
|
118
65
|
}
|
|
119
66
|
return null;
|
|
120
67
|
}
|
|
121
|
-
function getReferencedObject(node) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
68
|
+
function getReferencedObject(node, visitedVariables = new Set(), visitedNodes = new Set()) {
|
|
69
|
+
const current = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(node);
|
|
70
|
+
if (visitedNodes.has(current))
|
|
71
|
+
return null;
|
|
72
|
+
visitedNodes.add(current);
|
|
73
|
+
if (current.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
|
|
74
|
+
current.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
75
|
+
return current;
|
|
76
|
+
}
|
|
77
|
+
if (isIdentifier(current)) {
|
|
78
|
+
const variable = getVariable(current);
|
|
79
|
+
if (variable &&
|
|
80
|
+
!visitedVariables.has(variable) &&
|
|
81
|
+
variable.defs.length > 0) {
|
|
82
|
+
visitedVariables.add(variable);
|
|
83
|
+
const def = variable.defs[0];
|
|
84
|
+
if (def.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
85
|
+
def.node.init) {
|
|
86
|
+
const result = getReferencedObject(def.node.init, visitedVariables, visitedNodes);
|
|
87
|
+
if (result)
|
|
88
|
+
return result;
|
|
137
89
|
}
|
|
138
90
|
}
|
|
139
91
|
}
|
|
140
|
-
else if (
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
92
|
+
else if (current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
93
|
+
const object = current.object;
|
|
94
|
+
const property = current.property;
|
|
95
|
+
const referencedObj = getReferencedObject(object, visitedVariables, visitedNodes);
|
|
96
|
+
if (referencedObj) {
|
|
97
|
+
const info = objectMap.get(referencedObj);
|
|
98
|
+
let propValue;
|
|
99
|
+
const key = !current.computed && isIdentifier(property)
|
|
100
|
+
? property.name
|
|
101
|
+
: current.computed && property.type === utils_1.AST_NODE_TYPES.Literal
|
|
102
|
+
? property.value
|
|
103
|
+
: null;
|
|
104
|
+
if (key !== null &&
|
|
105
|
+
(typeof key === 'string' || typeof key === 'number')) {
|
|
106
|
+
// 1. Check assigned properties first (overrides literal properties)
|
|
107
|
+
if (info) {
|
|
108
|
+
propValue = info.assignedProperties.get(key);
|
|
109
|
+
}
|
|
110
|
+
// 2. Check object literal properties
|
|
111
|
+
if (!propValue &&
|
|
112
|
+
referencedObj.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
113
|
+
const prop = referencedObj.properties.find((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
|
|
114
|
+
!p.computed &&
|
|
115
|
+
((isIdentifier(p.key) && p.key.name === key) ||
|
|
116
|
+
(p.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
117
|
+
p.key.value === key)));
|
|
118
|
+
if (prop)
|
|
119
|
+
propValue = prop.value;
|
|
120
|
+
}
|
|
121
|
+
// 3. Check array literal elements
|
|
122
|
+
if (!propValue &&
|
|
123
|
+
referencedObj.type === utils_1.AST_NODE_TYPES.ArrayExpression &&
|
|
124
|
+
typeof key === 'number') {
|
|
125
|
+
const element = referencedObj.elements[key];
|
|
126
|
+
if (element && element.type !== utils_1.AST_NODE_TYPES.SpreadElement) {
|
|
127
|
+
propValue = element;
|
|
161
128
|
}
|
|
162
129
|
}
|
|
163
130
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return getReferencedObject(current);
|
|
175
|
-
}
|
|
176
|
-
if (isThisExpression(current)) {
|
|
177
|
-
const scope = context.getScope();
|
|
178
|
-
const scopeId = getScopeId(scope);
|
|
179
|
-
const scopeObjects = scopeMap.get(scopeId);
|
|
180
|
-
if (scopeObjects) {
|
|
181
|
-
for (const obj of scopeObjects) {
|
|
182
|
-
const info = objectMap.get(obj);
|
|
183
|
-
if (info && info.scope === scopeId && !info.isCircular) {
|
|
184
|
-
return obj;
|
|
131
|
+
if (propValue) {
|
|
132
|
+
const unwrappedValue = getUnwrappedObjectOrArray(propValue);
|
|
133
|
+
if (unwrappedValue)
|
|
134
|
+
return unwrappedValue;
|
|
135
|
+
if (isIdentifier(propValue) ||
|
|
136
|
+
propValue.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
137
|
+
return getReferencedObject(propValue, visitedVariables, visitedNodes);
|
|
138
|
+
}
|
|
139
|
+
if (isFunction(propValue) || isPrimitive(propValue)) {
|
|
140
|
+
return null;
|
|
185
141
|
}
|
|
186
142
|
}
|
|
187
143
|
}
|
|
@@ -189,392 +145,99 @@ exports.noCircularReferences = (0, createRule_1.createRule)({
|
|
|
189
145
|
return null;
|
|
190
146
|
}
|
|
191
147
|
function detectCircularReference(currentNode, visited = new Set(), depth = 0) {
|
|
192
|
-
if (depth >
|
|
193
|
-
return false;
|
|
194
|
-
if (visited.has(currentNode))
|
|
148
|
+
if (depth > 50)
|
|
149
|
+
return false;
|
|
150
|
+
if (visited.has(currentNode))
|
|
195
151
|
return true;
|
|
196
|
-
}
|
|
197
152
|
const objectInfo = objectMap.get(currentNode);
|
|
198
|
-
if (!objectInfo)
|
|
153
|
+
if (!objectInfo)
|
|
199
154
|
return false;
|
|
200
|
-
}
|
|
201
155
|
visited.add(currentNode);
|
|
202
156
|
for (const ref of objectInfo.references) {
|
|
203
157
|
const referencedObj = getReferencedObject(ref);
|
|
204
158
|
if (referencedObj &&
|
|
205
159
|
detectCircularReference(referencedObj, new Set(visited), depth + 1)) {
|
|
206
|
-
objectInfo.isCircular = true;
|
|
207
|
-
circularRefs.add(ref);
|
|
208
160
|
return true;
|
|
209
161
|
}
|
|
210
162
|
}
|
|
211
163
|
return false;
|
|
212
164
|
}
|
|
213
165
|
function checkAndReportCircularReference(targetObj, reference) {
|
|
214
|
-
const
|
|
166
|
+
const unwrapped = getUnwrappedObjectOrArray(targetObj);
|
|
167
|
+
if (!unwrapped)
|
|
168
|
+
return;
|
|
169
|
+
const targetInfo = objectMap.get(unwrapped);
|
|
215
170
|
if (targetInfo) {
|
|
216
171
|
targetInfo.references.add(reference);
|
|
217
|
-
if (detectCircularReference(
|
|
172
|
+
if (detectCircularReference(unwrapped)) {
|
|
218
173
|
reportCircularReference(reference, reference);
|
|
219
174
|
}
|
|
220
175
|
}
|
|
221
176
|
}
|
|
222
177
|
return {
|
|
223
178
|
ObjectExpression(node) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
179
|
+
objectMap.set(node, {
|
|
180
|
+
references: new Set(),
|
|
181
|
+
assignedProperties: new Map(),
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
ArrayExpression(node) {
|
|
185
|
+
objectMap.set(node, {
|
|
186
|
+
references: new Set(),
|
|
187
|
+
assignedProperties: new Map(),
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
'ArrayExpression > *'(node) {
|
|
191
|
+
if (!node || node.type === utils_1.AST_NODE_TYPES.SpreadElement)
|
|
192
|
+
return;
|
|
193
|
+
// Primitive values can never form circular references.
|
|
194
|
+
if (isPrimitive(node))
|
|
195
|
+
return;
|
|
196
|
+
const parentArray = node.parent;
|
|
197
|
+
const referencedObj = getReferencedObject(node);
|
|
198
|
+
if (referencedObj) {
|
|
199
|
+
checkAndReportCircularReference(parentArray, node);
|
|
231
200
|
}
|
|
232
|
-
scopeObjects.add(node);
|
|
233
201
|
},
|
|
234
202
|
'ObjectExpression > Property'(node) {
|
|
235
203
|
const parentObject = node.parent;
|
|
236
204
|
const value = node.value;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
205
|
+
// Primitive values (undefined, null, literals) can never form circular
|
|
206
|
+
// references, so skip them before any deeper resolution.
|
|
207
|
+
if (isPrimitive(value))
|
|
208
|
+
return;
|
|
209
|
+
const referencedObj = getReferencedObject(value);
|
|
210
|
+
if (referencedObj) {
|
|
211
|
+
checkAndReportCircularReference(parentObject, value);
|
|
242
212
|
}
|
|
243
213
|
},
|
|
244
214
|
AssignmentExpression(node) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
215
|
+
const right = node.right;
|
|
216
|
+
const left = node.left;
|
|
217
|
+
if (left.type !== utils_1.AST_NODE_TYPES.MemberExpression)
|
|
218
|
+
return;
|
|
219
|
+
const targetObj = getReferencedObject(left.object);
|
|
220
|
+
if (!targetObj)
|
|
221
|
+
return;
|
|
222
|
+
// Reassignments should override literal properties during resolution.
|
|
223
|
+
const targetInfo = objectMap.get(targetObj);
|
|
224
|
+
if (targetInfo) {
|
|
225
|
+
if (!left.computed && isIdentifier(left.property)) {
|
|
226
|
+
targetInfo.assignedProperties.set(left.property.name, right);
|
|
253
227
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const targetObj = getObjectFromMemberExpression(node.left);
|
|
260
|
-
if (targetObj) {
|
|
261
|
-
checkAndReportCircularReference(targetObj, node.right);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
VariableDeclarator(node) {
|
|
267
|
-
if (node.init?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
268
|
-
const properties = node.init.properties;
|
|
269
|
-
for (const prop of properties) {
|
|
270
|
-
if (prop.type === utils_1.AST_NODE_TYPES.Property &&
|
|
271
|
-
prop.value.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
272
|
-
const referencedObj = getReferencedObject(prop.value);
|
|
273
|
-
if (referencedObj) {
|
|
274
|
-
checkAndReportCircularReference(node.init, prop.value);
|
|
275
|
-
}
|
|
228
|
+
else if (left.computed &&
|
|
229
|
+
left.property.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
230
|
+
const key = left.property.value;
|
|
231
|
+
if (typeof key === 'string' || typeof key === 'number') {
|
|
232
|
+
targetInfo.assignedProperties.set(key, right);
|
|
276
233
|
}
|
|
277
234
|
}
|
|
278
235
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const body = node.value.body;
|
|
283
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
284
|
-
for (const stmt of body.body) {
|
|
285
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
286
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
287
|
-
const assignment = stmt.expression;
|
|
288
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
289
|
-
const targetObj = getObjectFromMemberExpression(assignment.left);
|
|
290
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
291
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
292
|
-
if (targetObj && referencedObj) {
|
|
293
|
-
const leftProperty = assignment.left.property;
|
|
294
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
295
|
-
leftProperty.name === 'self') {
|
|
296
|
-
const rightObj = getReferencedObject(assignment.right);
|
|
297
|
-
if (rightObj) {
|
|
298
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
else if (assignment.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
304
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
305
|
-
if (targetObj && referencedObj) {
|
|
306
|
-
const leftProperty = assignment.left.property;
|
|
307
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
308
|
-
leftProperty.name === 'self') {
|
|
309
|
-
const rightObj = getObjectFromMemberExpression(assignment.right);
|
|
310
|
-
if (rightObj) {
|
|
311
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
},
|
|
322
|
-
ClassDeclaration(node) {
|
|
323
|
-
const scope = context.getScope();
|
|
324
|
-
const scopeId = getScopeId(scope);
|
|
325
|
-
objectMap.set(node, { node, references: new Set(), scope: scopeId });
|
|
326
|
-
let scopeObjects = scopeMap.get(scopeId);
|
|
327
|
-
if (!scopeObjects) {
|
|
328
|
-
scopeObjects = new Set();
|
|
329
|
-
scopeMap.set(scopeId, scopeObjects);
|
|
330
|
-
}
|
|
331
|
-
scopeObjects.add(node);
|
|
332
|
-
// Check for circular references in constructor
|
|
333
|
-
const constructor = node.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
334
|
-
member.kind === 'constructor');
|
|
335
|
-
if (constructor) {
|
|
336
|
-
const body = constructor.value.body;
|
|
337
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
338
|
-
for (const stmt of body.body) {
|
|
339
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
340
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
341
|
-
const assignment = stmt.expression;
|
|
342
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
343
|
-
const targetObj = getObjectFromMemberExpression(assignment.left);
|
|
344
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
345
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
346
|
-
if (targetObj && referencedObj) {
|
|
347
|
-
const leftProperty = assignment.left.property;
|
|
348
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
349
|
-
leftProperty.name === 'self') {
|
|
350
|
-
const rightObj = getReferencedObject(assignment.right);
|
|
351
|
-
if (rightObj) {
|
|
352
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
else if (assignment.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
358
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
359
|
-
if (targetObj && referencedObj) {
|
|
360
|
-
const leftProperty = assignment.left.property;
|
|
361
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
362
|
-
leftProperty.name === 'self') {
|
|
363
|
-
const rightObj = getObjectFromMemberExpression(assignment.right);
|
|
364
|
-
if (rightObj) {
|
|
365
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
},
|
|
376
|
-
ClassExpression(node) {
|
|
377
|
-
const scope = context.getScope();
|
|
378
|
-
const scopeId = getScopeId(scope);
|
|
379
|
-
objectMap.set(node, { node, references: new Set(), scope: scopeId });
|
|
380
|
-
let scopeObjects = scopeMap.get(scopeId);
|
|
381
|
-
if (!scopeObjects) {
|
|
382
|
-
scopeObjects = new Set();
|
|
383
|
-
scopeMap.set(scopeId, scopeObjects);
|
|
384
|
-
}
|
|
385
|
-
scopeObjects.add(node);
|
|
386
|
-
// Check for circular references in constructor
|
|
387
|
-
const constructor = node.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
388
|
-
member.kind === 'constructor');
|
|
389
|
-
if (constructor) {
|
|
390
|
-
const body = constructor.value.body;
|
|
391
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
392
|
-
for (const stmt of body.body) {
|
|
393
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
394
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
395
|
-
const assignment = stmt.expression;
|
|
396
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
397
|
-
const targetObj = getObjectFromMemberExpression(assignment.left);
|
|
398
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
399
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
400
|
-
if (targetObj && referencedObj) {
|
|
401
|
-
const leftProperty = assignment.left.property;
|
|
402
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
403
|
-
leftProperty.name === 'self') {
|
|
404
|
-
const rightObj = getReferencedObject(assignment.right);
|
|
405
|
-
if (rightObj) {
|
|
406
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
else if (assignment.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
412
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
413
|
-
if (targetObj && referencedObj) {
|
|
414
|
-
const leftProperty = assignment.left.property;
|
|
415
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
416
|
-
leftProperty.name === 'self') {
|
|
417
|
-
const rightObj = getObjectFromMemberExpression(assignment.right);
|
|
418
|
-
if (rightObj) {
|
|
419
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
NewExpression(node) {
|
|
431
|
-
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
432
|
-
const scope = context.getScope();
|
|
433
|
-
const variable = scope.variables.find((v) => node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
434
|
-
v.name === node.callee.name);
|
|
435
|
-
if (variable?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.ClassDeclaration) {
|
|
436
|
-
const classDecl = variable.defs[0].node;
|
|
437
|
-
const constructor = classDecl.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
438
|
-
member.kind === 'constructor');
|
|
439
|
-
if (constructor) {
|
|
440
|
-
const body = constructor.value.body;
|
|
441
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
442
|
-
for (const stmt of body.body) {
|
|
443
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
444
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
445
|
-
const assignment = stmt.expression;
|
|
446
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
447
|
-
const leftObj = assignment.left.object;
|
|
448
|
-
if (leftObj.type === utils_1.AST_NODE_TYPES.ThisExpression) {
|
|
449
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
450
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
451
|
-
if (referencedObj) {
|
|
452
|
-
checkAndReportCircularReference(node, assignment.right);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
else if (assignment.right.type ===
|
|
456
|
-
utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
457
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
458
|
-
if (referencedObj) {
|
|
459
|
-
checkAndReportCircularReference(node, assignment.right);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
|
-
CallExpression(node) {
|
|
472
|
-
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
473
|
-
const obj = node.callee.object;
|
|
474
|
-
const prop = node.callee.property;
|
|
475
|
-
if (isIdentifier(obj) && isIdentifier(prop) && prop.name === 'then') {
|
|
476
|
-
// Handle Promise.then() calls
|
|
477
|
-
const scope = context.getScope();
|
|
478
|
-
const variable = scope.variables.find((v) => v.name === obj.name);
|
|
479
|
-
if (variable?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
480
|
-
const init = variable.defs[0].node.init;
|
|
481
|
-
if (init && init.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
482
|
-
const callee = init.callee;
|
|
483
|
-
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
484
|
-
const calleeObj = callee.object;
|
|
485
|
-
const calleeProp = callee.property;
|
|
486
|
-
if (isIdentifier(calleeObj) &&
|
|
487
|
-
isIdentifier(calleeProp) &&
|
|
488
|
-
calleeObj.name === 'Promise' &&
|
|
489
|
-
calleeProp.name === 'resolve') {
|
|
490
|
-
// This is a Promise.resolve() call
|
|
491
|
-
if (init.arguments.length > 0 &&
|
|
492
|
-
init.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
493
|
-
const arg = init.arguments[0];
|
|
494
|
-
const referencedObj = getReferencedObject(arg);
|
|
495
|
-
if (referencedObj) {
|
|
496
|
-
// Check if the promise callback assigns the resolved value back to the original object
|
|
497
|
-
if (node.arguments.length > 0 &&
|
|
498
|
-
node.arguments[0].type ===
|
|
499
|
-
utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
500
|
-
const callback = node.arguments[0];
|
|
501
|
-
if (callback.body.type ===
|
|
502
|
-
utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
503
|
-
const assignment = callback.body;
|
|
504
|
-
if (assignment.left.type ===
|
|
505
|
-
utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
506
|
-
const leftObj = assignment.left.object;
|
|
507
|
-
if (isIdentifier(leftObj)) {
|
|
508
|
-
const leftObjRef = getReferencedObject(leftObj);
|
|
509
|
-
if (leftObjRef === referencedObj) {
|
|
510
|
-
const reference = assignment.right;
|
|
511
|
-
reportCircularReference(assignment, reference);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
else if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
517
|
-
for (const stmt of callback.body.body) {
|
|
518
|
-
if (stmt.type ===
|
|
519
|
-
utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
520
|
-
stmt.expression.type ===
|
|
521
|
-
utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
522
|
-
const assignment = stmt.expression;
|
|
523
|
-
if (assignment.left.type ===
|
|
524
|
-
utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
525
|
-
const leftObj = assignment.left.object;
|
|
526
|
-
if (isIdentifier(leftObj)) {
|
|
527
|
-
const leftObjRef = getReferencedObject(leftObj);
|
|
528
|
-
if (leftObjRef === referencedObj) {
|
|
529
|
-
const reference = assignment.right;
|
|
530
|
-
reportCircularReference(assignment, reference);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
},
|
|
547
|
-
MemberExpression(node) {
|
|
548
|
-
if (node.parent &&
|
|
549
|
-
node.parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
550
|
-
node.parent.left === node) {
|
|
551
|
-
return; // Skip left side of assignments, handled elsewhere
|
|
552
|
-
}
|
|
553
|
-
const obj = node.object;
|
|
554
|
-
const prop = node.property;
|
|
555
|
-
if (isIdentifier(obj) && isIdentifier(prop)) {
|
|
556
|
-
const referencedObj = getReferencedObject(obj);
|
|
236
|
+
// Primitive values on the right-hand side can never create circular references.
|
|
237
|
+
if (!isPrimitive(right)) {
|
|
238
|
+
const referencedObj = getReferencedObject(right);
|
|
557
239
|
if (referencedObj) {
|
|
558
|
-
|
|
559
|
-
const scopeId = getScopeId(scope);
|
|
560
|
-
const info = objectMap.get(referencedObj);
|
|
561
|
-
if (info && info.scope === scopeId) {
|
|
562
|
-
// Check if this property access might lead to a circular reference
|
|
563
|
-
if (prop.name === 'self' ||
|
|
564
|
-
prop.name === 'ref' ||
|
|
565
|
-
prop.name === 'circular') {
|
|
566
|
-
const parent = node.parent;
|
|
567
|
-
if (parent &&
|
|
568
|
-
parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
569
|
-
parent.right === node) {
|
|
570
|
-
const leftObj = getObjectFromMemberExpression(parent.left);
|
|
571
|
-
if (leftObj === referencedObj) {
|
|
572
|
-
const reference = node;
|
|
573
|
-
reportCircularReference(node, reference);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
240
|
+
checkAndReportCircularReference(targetObj, right);
|
|
578
241
|
}
|
|
579
242
|
}
|
|
580
243
|
},
|