@blumintinc/eslint-plugin-blumint 1.18.13 → 1.18.14
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/lib/index.js +1 -1
- package/lib/rules/no-entire-object-hook-deps.js +29 -32
- package/package.json +1 -1
- package/release-manifest.json +14 -0
package/lib/index.js
CHANGED
|
@@ -65,9 +65,7 @@ function unwrapExpression(expr) {
|
|
|
65
65
|
}
|
|
66
66
|
return current;
|
|
67
67
|
}
|
|
68
|
-
function getObjectUsagesInHook(hookBody, objectName
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
-
context) {
|
|
68
|
+
function getObjectUsagesInHook(hookBody, objectName) {
|
|
71
69
|
const usages = new Map(); // Track usage and its position
|
|
72
70
|
const visited = new Set();
|
|
73
71
|
let needsEntireObject = false;
|
|
@@ -133,8 +131,15 @@ context) {
|
|
|
133
131
|
const memberExpr = current;
|
|
134
132
|
// Handle computed properties (like array indices)
|
|
135
133
|
if (memberExpr.computed) {
|
|
136
|
-
//
|
|
137
|
-
|
|
134
|
+
// why: only a *literal* computed key (obj[0], obj['special-key'])
|
|
135
|
+
// narrows to a single, stable field. EVERY other computed key —
|
|
136
|
+
// Identifier (obj[i]), CallExpression (obj[assertSafe(i)]),
|
|
137
|
+
// BinaryExpression (obj[i+1]), MemberExpression (obj[keys[j]]),
|
|
138
|
+
// TSAsExpression (obj[k as K]), TemplateLiteral (obj[`row-${i}`]), etc.
|
|
139
|
+
// — is a dynamic access that can read arbitrary elements across an
|
|
140
|
+
// iteration. There is no single narrowable field, so the whole object
|
|
141
|
+
// is a legitimate dependency: resolve the base and mark it accordingly.
|
|
142
|
+
if (memberExpr.property.type !== utils_1.AST_NODE_TYPES.Literal) {
|
|
138
143
|
// Check if this is accessing our target object
|
|
139
144
|
let currentBase = unwrapExpression(memberExpr.object);
|
|
140
145
|
while (currentBase.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
@@ -142,35 +147,23 @@ context) {
|
|
|
142
147
|
}
|
|
143
148
|
if (currentBase.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
144
149
|
currentBase.name === objectName) {
|
|
145
|
-
// This is a computed property access on our target object,
|
|
150
|
+
// This is a dynamic computed property access on our target object,
|
|
151
|
+
// so we need the entire object (no narrowable field exists).
|
|
146
152
|
needsEntireObject = true;
|
|
147
153
|
}
|
|
148
154
|
return null;
|
|
149
155
|
}
|
|
150
156
|
// For computed properties with literals
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
parts.unshift(`[${JSON.stringify(literalProp.value)}]`);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
// For other computed properties, use a wildcard
|
|
161
|
-
parts.unshift('[*]');
|
|
162
|
-
}
|
|
157
|
+
const literalProp = memberExpr.property;
|
|
158
|
+
if (typeof literalProp.value === 'number') {
|
|
159
|
+
parts.unshift(`[${literalProp.value}]`);
|
|
160
|
+
}
|
|
161
|
+
else if (typeof literalProp.value === 'string') {
|
|
162
|
+
parts.unshift(`[${JSON.stringify(literalProp.value)}]`);
|
|
163
163
|
}
|
|
164
164
|
else {
|
|
165
|
-
// For other computed properties
|
|
166
|
-
|
|
167
|
-
const propertyText = context.sourceCode.getText(memberExpr.property);
|
|
168
|
-
parts.unshift(`[${propertyText}]`);
|
|
169
|
-
}
|
|
170
|
-
catch (e) {
|
|
171
|
-
// Fallback to wildcard if we can't get the source text
|
|
172
|
-
parts.unshift('[*]');
|
|
173
|
-
}
|
|
165
|
+
// For other literal computed properties (e.g. boolean/null), wildcard
|
|
166
|
+
parts.unshift('[*]');
|
|
174
167
|
}
|
|
175
168
|
}
|
|
176
169
|
else {
|
|
@@ -184,7 +177,7 @@ context) {
|
|
|
184
177
|
STRING_METHODS.has(memberExpr.property.name))) {
|
|
185
178
|
// Check if this is accessing our target object or a property of it
|
|
186
179
|
let currentBase = unwrapExpression(memberExpr.object);
|
|
187
|
-
|
|
180
|
+
const pathParts = [];
|
|
188
181
|
let hasOptionalChainingInMethod = false;
|
|
189
182
|
// Build the path to the array/string being accessed
|
|
190
183
|
while (currentBase.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
@@ -352,7 +345,8 @@ context) {
|
|
|
352
345
|
effectiveParent.type === utils_1.AST_NODE_TYPES.TSNonNullExpression)) {
|
|
353
346
|
effectiveParent = effectiveParent.parent;
|
|
354
347
|
}
|
|
355
|
-
const isIntermediate = effectiveParent &&
|
|
348
|
+
const isIntermediate = effectiveParent &&
|
|
349
|
+
effectiveParent.type === utils_1.AST_NODE_TYPES.MemberExpression;
|
|
356
350
|
if (!isIntermediate) {
|
|
357
351
|
// Check if this member expression involves our target object
|
|
358
352
|
let current = memberExpr;
|
|
@@ -361,9 +355,12 @@ context) {
|
|
|
361
355
|
// Walk up the member expression chain to see if it involves our target object
|
|
362
356
|
while (current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
363
357
|
const currentMember = current;
|
|
364
|
-
// Check if this level uses dynamic computed property access
|
|
358
|
+
// Check if this level uses dynamic computed property access.
|
|
359
|
+
// why: any non-literal computed key reads arbitrary elements, so it
|
|
360
|
+
// requires the entire object. Only a literal key (obj[0], obj['k'])
|
|
361
|
+
// narrows to a single, stable field.
|
|
365
362
|
if (currentMember.computed &&
|
|
366
|
-
currentMember.property.type
|
|
363
|
+
currentMember.property.type !== utils_1.AST_NODE_TYPES.Literal) {
|
|
367
364
|
hasDynamicComputed = true;
|
|
368
365
|
}
|
|
369
366
|
current = unwrapExpression(currentMember.object);
|
|
@@ -582,7 +579,7 @@ exports.noEntireObjectHookDeps = (0, createRule_1.createRule)({
|
|
|
582
579
|
}
|
|
583
580
|
}
|
|
584
581
|
// For testing without TypeScript services, we'll assume all identifiers are objects
|
|
585
|
-
const result = getObjectUsagesInHook(callbackArg.body, objectName
|
|
582
|
+
const result = getObjectUsagesInHook(callbackArg.body, objectName);
|
|
586
583
|
// If the object is not used at all, suggest removing it
|
|
587
584
|
if (result.notUsed) {
|
|
588
585
|
context.report({
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.18.14",
|
|
4
|
+
"date": "2026-07-13T08:30:23.444Z",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "no-entire-object-hook-deps",
|
|
8
|
+
"changeType": "fix",
|
|
9
|
+
"issues": [
|
|
10
|
+
1291
|
|
11
|
+
],
|
|
12
|
+
"summary": "treat non-literal computed keys as whole-object access (closes #1291)"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"version": "1.18.13",
|
|
4
18
|
"date": "2026-07-13T01:43:11.918Z",
|