@blumintinc/eslint-plugin-blumint 1.20.188 → 1.20.189

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 CHANGED
@@ -223,7 +223,7 @@ function noFrontendImportsFromFunctionsPatterns(pattern) {
223
223
  module.exports = {
224
224
  meta: {
225
225
  name: '@blumintinc/eslint-plugin-blumint',
226
- version: '1.20.188',
226
+ version: '1.20.189',
227
227
  },
228
228
  parseOptions: {
229
229
  ecmaVersion: 2020,
@@ -1,3 +1,4 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
1
2
  type MessageIds = 'avoidEntireObject' | 'removeUnusedDependency';
2
- export declare const noEntireObjectHookDeps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
3
+ export declare const noEntireObjectHookDeps: TSESLint.RuleModule<MessageIds, [], TSESLint.RuleListener>;
3
4
  export {};
@@ -1167,6 +1167,48 @@ function getObjectUsagesInHook(hookBody, objectName, typeInfo, bodyIsDeferred =
1167
1167
  notUsed,
1168
1168
  };
1169
1169
  }
1170
+ /** The run of spaces/tabs opening the line that `offset` sits on. */
1171
+ function indentAt(text, offset) {
1172
+ const lineStart = text.lastIndexOf('\n', offset - 1) + 1;
1173
+ return /^[ \t]*/.exec(text.slice(lineStart, offset))?.[0] ?? '';
1174
+ }
1175
+ /**
1176
+ * Drop `span`, but re-emit any comment standing inside it.
1177
+ *
1178
+ * Removing a dependency means removing its separator too, so the span reaches
1179
+ * to a NEIGHBOURING element and therefore covers the margin between the two.
1180
+ * That margin is not the fixer's to delete: it can hold an
1181
+ * `eslint-disable-next-line` protecting the dependency that survives, and
1182
+ * dropping that directive silently re-enables another rule (#2208). Declining
1183
+ * the fix instead would only trade the lost comment for a transform that a
1184
+ * comment decides (#1877), so the comment is carried rather than obeyed.
1185
+ *
1186
+ * Each carried comment is re-emitted on a line of its own: a `//` comment
1187
+ * swallows whatever follows it on the same line, so folding one inline would
1188
+ * comment out the dependency that was meant to survive.
1189
+ */
1190
+ function removeCarryingComments(fixer, sourceCode, span, anchor) {
1191
+ const carried = sourceCode
1192
+ .getAllComments()
1193
+ .filter((comment) => comment.range[0] >= span[0] && comment.range[1] <= span[1]);
1194
+ // With nothing to preserve the span is pure separator and whitespace, so the
1195
+ // plain removal keeps the comment-free output exactly as it has always been.
1196
+ if (carried.length === 0) {
1197
+ return fixer.removeRange(span);
1198
+ }
1199
+ const text = sourceCode.getText();
1200
+ // Anchor the indentation on the end the surviving code sits against.
1201
+ const indent = indentAt(text, anchor === 'toNextElement' ? span[1] : carried[0].range[0]);
1202
+ const body = carried
1203
+ .map((comment) => text.slice(comment.range[0], comment.range[1]))
1204
+ .join(`\n${indent}`);
1205
+ // Both spellings close on a fresh line: whatever the span abutted — the next
1206
+ // dependency, or the separator trailing the last one — would otherwise land
1207
+ // on the final carried comment's line and be commented out.
1208
+ return fixer.replaceTextRange(span, anchor === 'toNextElement'
1209
+ ? `${body}\n${indent}`
1210
+ : `\n${indent}${body}\n${indent}`);
1211
+ }
1170
1212
  exports.noEntireObjectHookDeps = (0, createRule_1.createRule)({
1171
1213
  name: 'no-entire-object-hook-deps',
1172
1214
  meta: {
@@ -1339,21 +1381,19 @@ exports.noEntireObjectHookDeps = (0, createRule_1.createRule)({
1339
1381
  if (elementIndex === depsArg.elements.length - 1) {
1340
1382
  const prevElement = depsArg.elements[elementIndex - 1];
1341
1383
  if (prevElement) {
1342
- const range = [
1384
+ return removeCarryingComments(fixer, sourceCode, [
1343
1385
  prevElement.range[1],
1344
1386
  element.range[1],
1345
- ];
1346
- return fixer.removeRange(range);
1387
+ ], 'fromPrevElement');
1347
1388
  }
1348
1389
  }
1349
1390
  // Otherwise, remove the element and the following comma
1350
1391
  const nextElement = depsArg.elements[elementIndex + 1];
1351
1392
  if (nextElement) {
1352
- const range = [
1393
+ return removeCarryingComments(fixer, sourceCode, [
1353
1394
  element.range[0],
1354
1395
  nextElement.range[0],
1355
- ];
1356
- return fixer.removeRange(range);
1396
+ ], 'toNextElement');
1357
1397
  }
1358
1398
  // Fallback to just removing the element
1359
1399
  return fixer.remove(element);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.20.188",
3
+ "version": "1.20.189",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "version": "1.20.189",
4
+ "date": "2026-08-29T11:57:05.586Z",
5
+ "rules": [
6
+ {
7
+ "name": "no-entire-object-hook-deps",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 2208
11
+ ],
12
+ "summary": "carry comments in the removed dep's margin (closes #2208)"
13
+ }
14
+ ]
15
+ },
2
16
  {
3
17
  "version": "1.20.188",
4
18
  "date": "2026-08-29T07:11:55.294Z",