@blumintinc/eslint-plugin-blumint 1.14.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 +36 -14
- package/lib/index.js +20 -12
- package/lib/rules/avoid-utils-directory.js +0 -4
- package/lib/rules/consistent-callback-naming.js +68 -3
- package/lib/rules/dynamic-https-errors.d.ts +1 -1
- package/lib/rules/dynamic-https-errors.js +119 -49
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +1 -0
- package/lib/rules/enforce-boolean-naming-prefixes.js +86 -331
- package/lib/rules/enforce-date-ttime.d.ts +1 -0
- package/lib/rules/enforce-date-ttime.js +156 -0
- package/lib/rules/enforce-dynamic-firebase-imports.d.ts +2 -1
- package/lib/rules/enforce-dynamic-firebase-imports.js +3 -2
- package/lib/rules/enforce-dynamic-imports.d.ts +2 -1
- package/lib/rules/enforce-dynamic-imports.js +42 -21
- package/lib/rules/enforce-f-extension-for-entry-points.d.ts +8 -0
- package/lib/rules/enforce-f-extension-for-entry-points.js +283 -0
- package/lib/rules/enforce-global-constants.js +3 -3
- package/lib/rules/enforce-id-capitalization.js +1 -1
- package/lib/rules/enforce-memoize-async.js +66 -15
- package/lib/rules/enforce-mui-rounded-icons.js +42 -1
- package/lib/rules/enforce-props-argument-name.js +42 -16
- package/lib/rules/enforce-stable-hash-spread-props.js +3 -3
- package/lib/rules/enforce-transform-memoization.js +1 -1
- package/lib/rules/enforce-verb-noun-naming.js +3817 -4641
- package/lib/rules/global-const-style.js +25 -4
- package/lib/rules/logical-top-to-bottom-grouping.js +80 -2
- package/lib/rules/memo-compare-deeply-complex-props.js +183 -6
- package/lib/rules/memo-nested-react-components.js +243 -103
- package/lib/rules/no-array-length-in-deps.js +74 -3
- package/lib/rules/no-async-foreach.js +7 -2
- package/lib/rules/no-circular-references.d.ts +2 -1
- package/lib/rules/no-circular-references.js +150 -489
- package/lib/rules/no-compositing-layer-props.js +31 -0
- package/lib/rules/no-console-error.js +12 -10
- package/lib/rules/no-empty-dependency-use-callbacks.js +1 -1
- package/lib/rules/no-entire-object-hook-deps.js +147 -65
- package/lib/rules/no-excessive-parent-chain.js +3 -0
- package/lib/rules/no-explicit-return-type.js +6 -0
- package/lib/rules/no-hungarian.js +119 -24
- package/lib/rules/no-inline-component-prop.js +16 -7
- package/lib/rules/no-margin-properties.js +7 -38
- package/lib/rules/no-passthrough-getters.d.ts +2 -2
- package/lib/rules/no-passthrough-getters.js +83 -1
- package/lib/rules/no-redundant-this-params.js +50 -1
- package/lib/rules/no-unmemoized-memo-without-props.js +1 -1
- package/lib/rules/no-unnecessary-destructuring-rename.js +2 -5
- 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 +117 -54
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +109 -4
- package/lib/rules/prefer-params-over-parent-id.js +1 -1
- package/lib/rules/prefer-settings-object.js +27 -10
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +75 -4
- package/lib/rules/prefer-use-deep-compare-memo.js +8 -14
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +1 -1
- package/lib/rules/prevent-children-clobber.js +9 -5
- package/lib/rules/react-memoize-literals.js +218 -13
- package/lib/rules/require-https-error-cause.js +30 -11
- package/lib/rules/require-memo.js +17 -9
- 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 +49 -1
- package/lib/utils/ASTHelpers.js +394 -112
- 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,159 +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
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const scopeId = getScopeId(scope);
|
|
58
|
+
function getVariable(node) {
|
|
59
|
+
let scope = ASTHelpers_1.ASTHelpers.getScope(context, node);
|
|
60
|
+
while (scope) {
|
|
114
61
|
const variable = scope.variables.find((v) => v.name === node.name);
|
|
115
|
-
if (variable
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (isObjectExpression(init)) {
|
|
119
|
-
return init;
|
|
120
|
-
}
|
|
121
|
-
if (isFunction(init) ||
|
|
122
|
-
isArray(init) ||
|
|
123
|
-
isClass(init) ||
|
|
124
|
-
isPromise(init) ||
|
|
125
|
-
isPrimitive(init)) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// Check objects in the current scope
|
|
131
|
-
const scopeObjects = scopeMap.get(scopeId);
|
|
132
|
-
if (scopeObjects) {
|
|
133
|
-
for (const obj of scopeObjects) {
|
|
134
|
-
const info = objectMap.get(obj);
|
|
135
|
-
if (info && info.scope === scopeId && !info.isCircular) {
|
|
136
|
-
return obj;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
else if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
142
|
-
const property = node.property;
|
|
143
|
-
if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
144
|
-
const object = node.object;
|
|
145
|
-
if (isIdentifier(object)) {
|
|
146
|
-
const scope = context.getScope();
|
|
147
|
-
const variable = scope.variables.find((v) => v.name === object.name);
|
|
148
|
-
if (variable?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
149
|
-
const init = variable.defs[0].node.init;
|
|
150
|
-
if (init) {
|
|
151
|
-
if (isObjectExpression(init)) {
|
|
152
|
-
// Check if we're accessing a property that's a primitive or function
|
|
153
|
-
const prop = init.properties.find((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
|
|
154
|
-
p.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
155
|
-
p.key.name === property.name);
|
|
156
|
-
if (prop?.value) {
|
|
157
|
-
if (isFunction(prop.value) || isPrimitive(prop.value)) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return init;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
62
|
+
if (variable)
|
|
63
|
+
return variable;
|
|
64
|
+
scope = scope.upper;
|
|
167
65
|
}
|
|
168
66
|
return null;
|
|
169
67
|
}
|
|
170
|
-
function
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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;
|
|
174
76
|
}
|
|
175
77
|
if (isIdentifier(current)) {
|
|
176
|
-
|
|
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;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
177
91
|
}
|
|
178
|
-
if (
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
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;
|
|
187
141
|
}
|
|
188
142
|
}
|
|
189
143
|
}
|
|
@@ -191,392 +145,99 @@ exports.noCircularReferences = (0, createRule_1.createRule)({
|
|
|
191
145
|
return null;
|
|
192
146
|
}
|
|
193
147
|
function detectCircularReference(currentNode, visited = new Set(), depth = 0) {
|
|
194
|
-
if (depth >
|
|
195
|
-
return false;
|
|
196
|
-
if (visited.has(currentNode))
|
|
148
|
+
if (depth > 50)
|
|
149
|
+
return false;
|
|
150
|
+
if (visited.has(currentNode))
|
|
197
151
|
return true;
|
|
198
|
-
}
|
|
199
152
|
const objectInfo = objectMap.get(currentNode);
|
|
200
|
-
if (!objectInfo)
|
|
153
|
+
if (!objectInfo)
|
|
201
154
|
return false;
|
|
202
|
-
}
|
|
203
155
|
visited.add(currentNode);
|
|
204
156
|
for (const ref of objectInfo.references) {
|
|
205
157
|
const referencedObj = getReferencedObject(ref);
|
|
206
158
|
if (referencedObj &&
|
|
207
159
|
detectCircularReference(referencedObj, new Set(visited), depth + 1)) {
|
|
208
|
-
objectInfo.isCircular = true;
|
|
209
|
-
circularRefs.add(ref);
|
|
210
160
|
return true;
|
|
211
161
|
}
|
|
212
162
|
}
|
|
213
163
|
return false;
|
|
214
164
|
}
|
|
215
165
|
function checkAndReportCircularReference(targetObj, reference) {
|
|
216
|
-
const
|
|
166
|
+
const unwrapped = getUnwrappedObjectOrArray(targetObj);
|
|
167
|
+
if (!unwrapped)
|
|
168
|
+
return;
|
|
169
|
+
const targetInfo = objectMap.get(unwrapped);
|
|
217
170
|
if (targetInfo) {
|
|
218
171
|
targetInfo.references.add(reference);
|
|
219
|
-
if (detectCircularReference(
|
|
172
|
+
if (detectCircularReference(unwrapped)) {
|
|
220
173
|
reportCircularReference(reference, reference);
|
|
221
174
|
}
|
|
222
175
|
}
|
|
223
176
|
}
|
|
224
177
|
return {
|
|
225
178
|
ObjectExpression(node) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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);
|
|
233
200
|
}
|
|
234
|
-
scopeObjects.add(node);
|
|
235
201
|
},
|
|
236
202
|
'ObjectExpression > Property'(node) {
|
|
237
203
|
const parentObject = node.parent;
|
|
238
204
|
const value = node.value;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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);
|
|
244
212
|
}
|
|
245
213
|
},
|
|
246
214
|
AssignmentExpression(node) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (referencedObj &&
|
|
260
|
-
node.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
261
|
-
const targetObj = getObjectFromMemberExpression(node.left);
|
|
262
|
-
if (targetObj) {
|
|
263
|
-
checkAndReportCircularReference(targetObj, node.right);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
VariableDeclarator(node) {
|
|
269
|
-
if (node.init?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
270
|
-
const properties = node.init.properties;
|
|
271
|
-
for (const prop of properties) {
|
|
272
|
-
if (prop.type === utils_1.AST_NODE_TYPES.Property &&
|
|
273
|
-
prop.value.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
274
|
-
const referencedObj = getReferencedObject(prop.value);
|
|
275
|
-
if (referencedObj) {
|
|
276
|
-
checkAndReportCircularReference(node.init, prop.value);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
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);
|
|
279
227
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
286
|
-
for (const stmt of body.body) {
|
|
287
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
288
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
289
|
-
const assignment = stmt.expression;
|
|
290
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
291
|
-
const targetObj = getObjectFromMemberExpression(assignment.left);
|
|
292
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
293
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
294
|
-
if (targetObj && referencedObj) {
|
|
295
|
-
const leftProperty = assignment.left.property;
|
|
296
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
297
|
-
leftProperty.name === 'self') {
|
|
298
|
-
const rightObj = getReferencedObject(assignment.right);
|
|
299
|
-
if (rightObj) {
|
|
300
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
else if (assignment.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
306
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
307
|
-
if (targetObj && referencedObj) {
|
|
308
|
-
const leftProperty = assignment.left.property;
|
|
309
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
310
|
-
leftProperty.name === 'self') {
|
|
311
|
-
const rightObj = getObjectFromMemberExpression(assignment.right);
|
|
312
|
-
if (rightObj) {
|
|
313
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
ClassDeclaration(node) {
|
|
325
|
-
const scope = context.getScope();
|
|
326
|
-
const scopeId = getScopeId(scope);
|
|
327
|
-
objectMap.set(node, { node, references: new Set(), scope: scopeId });
|
|
328
|
-
let scopeObjects = scopeMap.get(scopeId);
|
|
329
|
-
if (!scopeObjects) {
|
|
330
|
-
scopeObjects = new Set();
|
|
331
|
-
scopeMap.set(scopeId, scopeObjects);
|
|
332
|
-
}
|
|
333
|
-
scopeObjects.add(node);
|
|
334
|
-
// Check for circular references in constructor
|
|
335
|
-
const constructor = node.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
336
|
-
member.kind === 'constructor');
|
|
337
|
-
if (constructor) {
|
|
338
|
-
const body = constructor.value.body;
|
|
339
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
340
|
-
for (const stmt of body.body) {
|
|
341
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
342
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
343
|
-
const assignment = stmt.expression;
|
|
344
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
345
|
-
const targetObj = getObjectFromMemberExpression(assignment.left);
|
|
346
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
347
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
348
|
-
if (targetObj && referencedObj) {
|
|
349
|
-
const leftProperty = assignment.left.property;
|
|
350
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
351
|
-
leftProperty.name === 'self') {
|
|
352
|
-
const rightObj = getReferencedObject(assignment.right);
|
|
353
|
-
if (rightObj) {
|
|
354
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
else if (assignment.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
360
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
361
|
-
if (targetObj && referencedObj) {
|
|
362
|
-
const leftProperty = assignment.left.property;
|
|
363
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
364
|
-
leftProperty.name === 'self') {
|
|
365
|
-
const rightObj = getObjectFromMemberExpression(assignment.right);
|
|
366
|
-
if (rightObj) {
|
|
367
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
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);
|
|
374
233
|
}
|
|
375
234
|
}
|
|
376
235
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const scopeId = getScopeId(scope);
|
|
381
|
-
objectMap.set(node, { node, references: new Set(), scope: scopeId });
|
|
382
|
-
let scopeObjects = scopeMap.get(scopeId);
|
|
383
|
-
if (!scopeObjects) {
|
|
384
|
-
scopeObjects = new Set();
|
|
385
|
-
scopeMap.set(scopeId, scopeObjects);
|
|
386
|
-
}
|
|
387
|
-
scopeObjects.add(node);
|
|
388
|
-
// Check for circular references in constructor
|
|
389
|
-
const constructor = node.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
390
|
-
member.kind === 'constructor');
|
|
391
|
-
if (constructor) {
|
|
392
|
-
const body = constructor.value.body;
|
|
393
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
394
|
-
for (const stmt of body.body) {
|
|
395
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
396
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
397
|
-
const assignment = stmt.expression;
|
|
398
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
399
|
-
const targetObj = getObjectFromMemberExpression(assignment.left);
|
|
400
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
401
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
402
|
-
if (targetObj && referencedObj) {
|
|
403
|
-
const leftProperty = assignment.left.property;
|
|
404
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
405
|
-
leftProperty.name === 'self') {
|
|
406
|
-
const rightObj = getReferencedObject(assignment.right);
|
|
407
|
-
if (rightObj) {
|
|
408
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
else if (assignment.right.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
414
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
415
|
-
if (targetObj && referencedObj) {
|
|
416
|
-
const leftProperty = assignment.left.property;
|
|
417
|
-
if (leftProperty.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
418
|
-
leftProperty.name === 'self') {
|
|
419
|
-
const rightObj = getObjectFromMemberExpression(assignment.right);
|
|
420
|
-
if (rightObj) {
|
|
421
|
-
checkAndReportCircularReference(targetObj, assignment.right);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
NewExpression(node) {
|
|
433
|
-
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
434
|
-
const scope = context.getScope();
|
|
435
|
-
const variable = scope.variables.find((v) => node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
436
|
-
v.name === node.callee.name);
|
|
437
|
-
if (variable?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.ClassDeclaration) {
|
|
438
|
-
const classDecl = variable.defs[0].node;
|
|
439
|
-
const constructor = classDecl.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
440
|
-
member.kind === 'constructor');
|
|
441
|
-
if (constructor) {
|
|
442
|
-
const body = constructor.value.body;
|
|
443
|
-
if (body && body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
444
|
-
for (const stmt of body.body) {
|
|
445
|
-
if (stmt.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
446
|
-
stmt.expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
447
|
-
const assignment = stmt.expression;
|
|
448
|
-
if (assignment.left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
449
|
-
const leftObj = assignment.left.object;
|
|
450
|
-
if (leftObj.type === utils_1.AST_NODE_TYPES.ThisExpression) {
|
|
451
|
-
if (assignment.right.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
452
|
-
const referencedObj = getReferencedObject(assignment.right);
|
|
453
|
-
if (referencedObj) {
|
|
454
|
-
checkAndReportCircularReference(node, assignment.right);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
else if (assignment.right.type ===
|
|
458
|
-
utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
459
|
-
const referencedObj = getObjectFromMemberExpression(assignment.right);
|
|
460
|
-
if (referencedObj) {
|
|
461
|
-
checkAndReportCircularReference(node, assignment.right);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
},
|
|
473
|
-
CallExpression(node) {
|
|
474
|
-
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
475
|
-
const obj = node.callee.object;
|
|
476
|
-
const prop = node.callee.property;
|
|
477
|
-
if (isIdentifier(obj) && isIdentifier(prop) && prop.name === 'then') {
|
|
478
|
-
// Handle Promise.then() calls
|
|
479
|
-
const scope = context.getScope();
|
|
480
|
-
const variable = scope.variables.find((v) => v.name === obj.name);
|
|
481
|
-
if (variable?.defs[0]?.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
482
|
-
const init = variable.defs[0].node.init;
|
|
483
|
-
if (init && init.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
484
|
-
const callee = init.callee;
|
|
485
|
-
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
486
|
-
const calleeObj = callee.object;
|
|
487
|
-
const calleeProp = callee.property;
|
|
488
|
-
if (isIdentifier(calleeObj) &&
|
|
489
|
-
isIdentifier(calleeProp) &&
|
|
490
|
-
calleeObj.name === 'Promise' &&
|
|
491
|
-
calleeProp.name === 'resolve') {
|
|
492
|
-
// This is a Promise.resolve() call
|
|
493
|
-
if (init.arguments.length > 0 &&
|
|
494
|
-
init.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
495
|
-
const arg = init.arguments[0];
|
|
496
|
-
const referencedObj = getReferencedObject(arg);
|
|
497
|
-
if (referencedObj) {
|
|
498
|
-
// Check if the promise callback assigns the resolved value back to the original object
|
|
499
|
-
if (node.arguments.length > 0 &&
|
|
500
|
-
node.arguments[0].type ===
|
|
501
|
-
utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
502
|
-
const callback = node.arguments[0];
|
|
503
|
-
if (callback.body.type ===
|
|
504
|
-
utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
505
|
-
const assignment = callback.body;
|
|
506
|
-
if (assignment.left.type ===
|
|
507
|
-
utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
508
|
-
const leftObj = assignment.left.object;
|
|
509
|
-
if (isIdentifier(leftObj)) {
|
|
510
|
-
const leftObjRef = getReferencedObject(leftObj);
|
|
511
|
-
if (leftObjRef === referencedObj) {
|
|
512
|
-
const reference = assignment.right;
|
|
513
|
-
reportCircularReference(assignment, reference);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
else if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
519
|
-
for (const stmt of callback.body.body) {
|
|
520
|
-
if (stmt.type ===
|
|
521
|
-
utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
522
|
-
stmt.expression.type ===
|
|
523
|
-
utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
|
524
|
-
const assignment = stmt.expression;
|
|
525
|
-
if (assignment.left.type ===
|
|
526
|
-
utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
527
|
-
const leftObj = assignment.left.object;
|
|
528
|
-
if (isIdentifier(leftObj)) {
|
|
529
|
-
const leftObjRef = getReferencedObject(leftObj);
|
|
530
|
-
if (leftObjRef === referencedObj) {
|
|
531
|
-
const reference = assignment.right;
|
|
532
|
-
reportCircularReference(assignment, reference);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
},
|
|
549
|
-
MemberExpression(node) {
|
|
550
|
-
if (node.parent &&
|
|
551
|
-
node.parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
552
|
-
node.parent.left === node) {
|
|
553
|
-
return; // Skip left side of assignments, handled elsewhere
|
|
554
|
-
}
|
|
555
|
-
const obj = node.object;
|
|
556
|
-
const prop = node.property;
|
|
557
|
-
if (isIdentifier(obj) && isIdentifier(prop)) {
|
|
558
|
-
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);
|
|
559
239
|
if (referencedObj) {
|
|
560
|
-
|
|
561
|
-
const scopeId = getScopeId(scope);
|
|
562
|
-
const info = objectMap.get(referencedObj);
|
|
563
|
-
if (info && info.scope === scopeId) {
|
|
564
|
-
// Check if this property access might lead to a circular reference
|
|
565
|
-
if (prop.name === 'self' ||
|
|
566
|
-
prop.name === 'ref' ||
|
|
567
|
-
prop.name === 'circular') {
|
|
568
|
-
const parent = node.parent;
|
|
569
|
-
if (parent &&
|
|
570
|
-
parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
571
|
-
parent.right === node) {
|
|
572
|
-
const leftObj = getObjectFromMemberExpression(parent.left);
|
|
573
|
-
if (leftObj === referencedObj) {
|
|
574
|
-
const reference = node;
|
|
575
|
-
reportCircularReference(node, reference);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
240
|
+
checkAndReportCircularReference(targetObj, right);
|
|
580
241
|
}
|
|
581
242
|
}
|
|
582
243
|
},
|