@blumintinc/eslint-plugin-blumint 1.20.93 → 1.20.95

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.93',
226
+ version: '1.20.95',
227
227
  },
228
228
  parseOptions: {
229
229
  ecmaVersion: 2020,
@@ -27,6 +27,7 @@ exports.enforceDateTTime = void 0;
27
27
  const utils_1 = require("@typescript-eslint/utils");
28
28
  const ts = __importStar(require("typescript"));
29
29
  const createRule_1 = require("../utils/createRule");
30
+ const disableDirectives_1 = require("../utils/disableDirectives");
30
31
  const importRemoval_1 = require("../utils/importRemoval");
31
32
  exports.enforceDateTTime = (0, createRule_1.createRule)({
32
33
  name: 'enforce-date-ttime',
@@ -92,7 +93,102 @@ exports.enforceDateTTime = (0, createRule_1.createRule)({
92
93
  }
93
94
  return false;
94
95
  }
96
+ /**
97
+ * Reporting is deferred to `Program:exit` because an import is unbound only
98
+ * once no reference to it survives the fix, and a file where two arguments
99
+ * name the same imported alias overwrites both in a single pass. Judging
100
+ * each rewrite alone sees the sibling argument still standing, concludes the
101
+ * binding is alive, and leaves the import stranded with no later pass to
102
+ * notice — this rule's own reports are resolved by the fix, so nothing
103
+ * re-reports the debt.
104
+ */
105
+ const sites = [];
106
+ /**
107
+ * Suppression is applied to reports after a rule emits them, so a suppressed
108
+ * site keeps its argument while losing its fix. Counting its range toward
109
+ * orphanhood would unbind an import the surviving text still spells, trading
110
+ * an unused import for a dangling type.
111
+ */
112
+ const isReportSuppressed = (0, disableDirectives_1.createSuppressionChecker)(context);
113
+ /**
114
+ * Supplies a TTime that was left out entirely. Declines whenever the
115
+ * argument cannot be appended positionally — a gap before TTime would need
116
+ * every intervening parameter spelled out, and this rule does not invent
117
+ * them.
118
+ */
119
+ function fixOmittedArgument(site, fixer) {
120
+ const { reference, tTimeIndex } = site;
121
+ if (reference.typeParameters) {
122
+ const { params } = reference.typeParameters;
123
+ if (tTimeIndex === params.length) {
124
+ return fixer.insertTextAfter(params[params.length - 1], ', Date');
125
+ }
126
+ return null;
127
+ }
128
+ if (tTimeIndex === 0) {
129
+ return fixer.insertTextAfter(reference.typeName, '<Date>');
130
+ }
131
+ return null;
132
+ }
133
+ /**
134
+ * The rewrites that actually ship. A site is excluded when its report will
135
+ * be suppressed, or when its own range holds the last reference to something
136
+ * the helper cannot rewrite — a locally declared alias, or the enclosing
137
+ * declaration's own type parameter. Deleting a declaration is a materially
138
+ * riskier edit than dropping an import specifier, and a type parameter left
139
+ * unread by its own body fails `no-unused-vars` and `noUnusedParameters`
140
+ * exactly as an orphaned alias does, on top of turning a pass-through
141
+ * generic into one whose argument no longer means anything.
142
+ *
143
+ * Screening individually before batching keeps one unfixable site from
144
+ * vetoing the rest: orphanhood grows monotonically with the overwritten set,
145
+ * so a site that cannot be planned alone can only ever poison the batch.
146
+ */
147
+ function selectRewritableSites() {
148
+ return sites.filter((site) => site.kind === 'nonDate' &&
149
+ !isReportSuppressed(site.reportNode) &&
150
+ (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, [site.reportNode.range]) !==
151
+ null);
152
+ }
95
153
  return {
154
+ 'Program:exit'() {
155
+ if (sites.length === 0)
156
+ return;
157
+ const rewrites = selectRewritableSites();
158
+ const ranges = rewrites.map((site) => site.reportNode.range);
159
+ // One plan over every surviving rewrite: an import referenced solely by
160
+ // arguments that all go in this pass is orphaned by their union, even
161
+ // though no single one of them orphans it.
162
+ const importRanges = ranges.length > 0
163
+ ? (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, ranges)
164
+ : null;
165
+ // The whole batch ships as one fix, so no rewrite can land without the
166
+ // others that the import's orphanhood was judged against. The rest
167
+ // report without a fixer; the carrier's pass already resolves them.
168
+ const carrier = importRanges ? rewrites[0] : undefined;
169
+ for (const site of sites) {
170
+ if (site.kind === 'omitted') {
171
+ context.report({
172
+ node: site.reportNode,
173
+ messageId: 'enforceDateTTime',
174
+ data: { typeName: site.typeName },
175
+ fix: (fixer) => fixOmittedArgument(site, fixer),
176
+ });
177
+ continue;
178
+ }
179
+ context.report({
180
+ node: site.reportNode,
181
+ messageId: 'enforceDateTTime',
182
+ data: { typeName: site.typeName },
183
+ fix: site === carrier && importRanges
184
+ ? (fixer) => [
185
+ ...rewrites.map((rewrite) => fixer.replaceText(rewrite.reportNode, 'Date')),
186
+ ...importRanges.map((range) => fixer.removeRange([range[0], range[1]])),
187
+ ]
188
+ : null,
189
+ });
190
+ }
191
+ },
96
192
  TSTypeReference(node) {
97
193
  const typeNameNode = node.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName
98
194
  ? node.typeName.right
@@ -116,71 +212,21 @@ exports.enforceDateTTime = (0, createRule_1.createRule)({
116
212
  const tTimeArg = typeArgs[tTimeIndex];
117
213
  const typeName = sourceCode.getText(node.typeName);
118
214
  if (!tTimeArg) {
119
- // TTime is omitted
120
- context.report({
121
- node,
122
- messageId: 'enforceDateTTime',
123
- data: { typeName },
124
- fix(fixer) {
125
- if (node.typeParameters) {
126
- // Already has type parameters, but not enough to cover TTime
127
- const lastParam = node.typeParameters.params[node.typeParameters.params.length - 1];
128
- // Check if we can just append ", Date"
129
- // We can only do this safely if all parameters between existing ones and TTime have defaults.
130
- // For simplicity and safety, we only fix if tTimeIndex is the next one or if it's already provided.
131
- if (tTimeIndex === node.typeParameters.params.length) {
132
- return fixer.insertTextAfter(lastParam, ', Date');
133
- }
134
- }
135
- else if (tTimeIndex === 0) {
136
- // No type parameters and TTime is the first one
137
- return fixer.insertTextAfter(node.typeName, '<Date>');
138
- }
139
- return null;
140
- },
215
+ sites.push({
216
+ kind: 'omitted',
217
+ reportNode: node,
218
+ reference: node,
219
+ typeName,
220
+ tTimeIndex,
141
221
  });
142
222
  }
143
223
  else if (!isExactDate(tTimeArg)) {
144
- // TTime is provided but is not exactly Date. Overwriting the argument
145
- // deletes every name it mentions, so the rewrite and the unbinding of
146
- // whatever it was the last reference to are one fix: applying either
147
- // half alone leaves the file worse than applying neither, and since
148
- // this rule's own report is resolved by the fix, nothing re-reports
149
- // the debt an orphaned declaration becomes.
150
- //
151
- // Orphanhood is judged against this one argument's own range and the
152
- // file as it stands, never against what the rest of the `--fix` run
153
- // might also overwrite. A sibling argument naming the same alias may
154
- // be `eslint-disable`d — which a rule cannot see, since suppression is
155
- // applied to reports after they are emitted — so an edit assuming its
156
- // sibling will also go unbinds an import the survivor still
157
- // references, trading an unused import for a dangling type. Judging
158
- // one edit at a time is suppression-safe by construction.
159
- const importRanges = (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, [
160
- tTimeArg.range,
161
- ]);
162
- context.report({
163
- node: tTimeArg,
164
- messageId: 'enforceDateTTime',
165
- data: { typeName },
166
- // No plan means the argument holds the last reference to something
167
- // the helper cannot rewrite — a locally declared alias, or the
168
- // enclosing declaration's own type parameter — so the argument
169
- // stays as written: the report without a fixer is the lesser
170
- // damage. Deleting a declaration is a materially riskier edit than
171
- // dropping an import specifier, and a type parameter left unread by
172
- // its own body fails `no-unused-vars` and `noUnusedParameters`
173
- // exactly as an orphaned alias does, on top of turning a
174
- // pass-through generic into one whose argument no longer means
175
- // anything.
176
- ...(importRanges
177
- ? {
178
- fix: (fixer) => [
179
- fixer.replaceText(tTimeArg, 'Date'),
180
- ...importRanges.map((range) => fixer.removeRange([range[0], range[1]])),
181
- ],
182
- }
183
- : {}),
224
+ sites.push({
225
+ kind: 'nonDate',
226
+ reportNode: tTimeArg,
227
+ reference: node,
228
+ typeName,
229
+ tTimeIndex,
184
230
  });
185
231
  }
186
232
  },
@@ -29,6 +29,7 @@ const visitor_keys_1 = require("@typescript-eslint/visitor-keys");
29
29
  const ts = __importStar(require("typescript"));
30
30
  const ASTHelpers_1 = require("../utils/ASTHelpers");
31
31
  const createRule_1 = require("../utils/createRule");
32
+ const disableDirectives_1 = require("../utils/disableDirectives");
32
33
  const importRemoval_1 = require("../utils/importRemoval");
33
34
  /**
34
35
  * Type string formatting flags chosen to keep comparisons stable and predictable.
@@ -345,51 +346,78 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
345
346
  }
346
347
  const checker = parserServices.program.getTypeChecker();
347
348
  /**
348
- * Reports the redundant annotation, taking with it any import it was the
349
- * only consumer of. The two are one fix: applying either half alone leaves
350
- * the file worse than applying neither a stripped annotation with its
351
- * import left behind fails `no-unused-vars`, and since this rule's own
352
- * report is resolved by the fix, nothing re-reports the debt.
353
- *
354
- * Orphanhood is judged against this one annotation's own removal and the
355
- * file as it stands, never against what the rest of the `--fix` run might
356
- * also delete. A sibling annotation naming the same type may be
357
- * `eslint-disable`d — which a rule cannot see, since suppression is applied
358
- * to reports after they are emitted — so an edit that assumes its sibling
359
- * will also go deletes an import the surviving annotation still references,
360
- * trading an unused import for a dangling type. Judging one edit at a time
361
- * is suppression-safe by construction: a suppressed report's fix never
362
- * applies, so it can never have been depended on.
363
- *
364
- * A type the annotation names but the helper cannot rewrite — a local alias,
365
- * an interface, a type parameter — declines the fix outright. Deleting a
366
- * declaration is a materially riskier edit than dropping an import
367
- * specifier, and the author is better placed to decide whether the type
368
- * should go or be used elsewhere.
349
+ * Reporting is deferred to `Program:exit` because an import is unbound only
350
+ * once no reference to it survives the fix, and a file where two annotations
351
+ * name the same imported type strips both in a single pass. Judging each
352
+ * removal alone sees the sibling annotation still standing, concludes the
353
+ * binding is alive, and leaves the import stranded with no later pass to
354
+ * notice — this rule's own reports are resolved by the fix, so nothing
355
+ * re-reports the debt.
369
356
  */
370
- function reportIfRedundant(annotation, assertion, reportNode, fixerTarget) {
357
+ const sites = [];
358
+ /**
359
+ * Suppression is applied to reports after a rule emits them, so a suppressed
360
+ * site keeps its annotation while losing its fix. Counting its removal
361
+ * toward orphanhood would unbind an import the surviving text still spells,
362
+ * trading an unused import for a dangling type.
363
+ */
364
+ const isReportSuppressed = (0, disableDirectives_1.createSuppressionChecker)(context);
365
+ function collectIfRedundant(annotation, assertion, reportNode, fixerTarget) {
371
366
  const matchingType = haveMatchingTypes(annotation.typeAnnotation, assertion, checker, parserServices);
372
367
  if (!matchingType)
373
368
  return;
374
- const removal = annotationRemovalRange(fixerTarget, sourceCode);
375
- const importRanges = (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, [removal]);
376
- context.report({
377
- node: reportNode,
378
- messageId: 'redundantAnnotationAndAssertion',
379
- data: { type: matchingType },
380
- // No plan means no binding can be unbound safely, so the annotation
381
- // stays too: the report without a fixer is the lesser damage.
382
- ...(importRanges
383
- ? {
384
- fix: (fixer) => [
385
- fixer.removeRange([removal[0], removal[1]]),
386
- ...importRanges.map((range) => fixer.removeRange([range[0], range[1]])),
387
- ],
388
- }
389
- : {}),
369
+ sites.push({
370
+ reportNode,
371
+ removal: annotationRemovalRange(fixerTarget, sourceCode),
372
+ matchingType,
390
373
  });
391
374
  }
375
+ /**
376
+ * The sites whose fixes actually ship. A site is excluded when its report
377
+ * will be suppressed, or when its own removal orphans something the helper
378
+ * cannot rewrite — a local alias, an interface, a type parameter. Deleting a
379
+ * declaration is a materially riskier edit than dropping an import
380
+ * specifier, and the author is better placed to decide whether the type
381
+ * should go or be used elsewhere.
382
+ *
383
+ * Screening individually before batching keeps one unfixable site from
384
+ * vetoing the rest: orphanhood grows monotonically with the removed set, so
385
+ * a site that cannot be planned alone can only ever poison the batch.
386
+ */
387
+ function selectFixableSites() {
388
+ return sites.filter((site) => !isReportSuppressed(site.reportNode) &&
389
+ (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, [site.removal]) !== null);
390
+ }
392
391
  return {
392
+ 'Program:exit'() {
393
+ if (sites.length === 0)
394
+ return;
395
+ const fixable = selectFixableSites();
396
+ const removals = fixable.map((site) => site.removal);
397
+ // One plan over every surviving removal: an import referenced solely by
398
+ // annotations that all go in this pass is orphaned by their union, even
399
+ // though no single one of them orphans it.
400
+ const importRanges = removals.length > 0
401
+ ? (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, removals)
402
+ : null;
403
+ // The whole batch ships as one fix, so no removal can land without the
404
+ // others that the import's orphanhood was judged against. The rest
405
+ // report without a fixer; the carrier's pass already resolves them.
406
+ const carrier = importRanges ? fixable[0] : undefined;
407
+ for (const site of sites) {
408
+ context.report({
409
+ node: site.reportNode,
410
+ messageId: 'redundantAnnotationAndAssertion',
411
+ data: { type: site.matchingType },
412
+ fix: site === carrier && importRanges
413
+ ? (fixer) => [
414
+ ...removals.map((range) => fixer.removeRange([range[0], range[1]])),
415
+ ...importRanges.map((range) => fixer.removeRange([range[0], range[1]])),
416
+ ]
417
+ : null,
418
+ });
419
+ }
420
+ },
393
421
  VariableDeclarator(node) {
394
422
  if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier ||
395
423
  !node.id.typeAnnotation ||
@@ -400,7 +428,7 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
400
428
  const assertionType = extractAssertionTypeNode(node.init);
401
429
  if (!assertionType)
402
430
  return;
403
- reportIfRedundant(node.id.typeAnnotation, assertionType, node.id, node.id.typeAnnotation);
431
+ collectIfRedundant(node.id.typeAnnotation, assertionType, node.id, node.id.typeAnnotation);
404
432
  },
405
433
  PropertyDefinition(node) {
406
434
  if (!node.typeAnnotation ||
@@ -411,7 +439,7 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
411
439
  const assertionType = extractAssertionTypeNode(node.value);
412
440
  if (!assertionType)
413
441
  return;
414
- reportIfRedundant(node.typeAnnotation, assertionType, node.key, node.typeAnnotation);
442
+ collectIfRedundant(node.typeAnnotation, assertionType, node.key, node.typeAnnotation);
415
443
  },
416
444
  FunctionDeclaration(node) {
417
445
  if (!node.returnType)
@@ -419,7 +447,7 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
419
447
  const assertionType = getReturnAssertion(node);
420
448
  if (!assertionType)
421
449
  return;
422
- reportIfRedundant(node.returnType, assertionType, node.id ?? node, node.returnType);
450
+ collectIfRedundant(node.returnType, assertionType, node.id ?? node, node.returnType);
423
451
  },
424
452
  FunctionExpression(node) {
425
453
  if (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
@@ -430,7 +458,7 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
430
458
  const assertionType = getReturnAssertion(node);
431
459
  if (!assertionType)
432
460
  return;
433
- reportIfRedundant(node.returnType, assertionType, node, node.returnType);
461
+ collectIfRedundant(node.returnType, assertionType, node, node.returnType);
434
462
  },
435
463
  ArrowFunctionExpression(node) {
436
464
  if (!node.returnType)
@@ -438,7 +466,7 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
438
466
  const assertionType = getReturnAssertion(node);
439
467
  if (!assertionType)
440
468
  return;
441
- reportIfRedundant(node.returnType, assertionType, node, node.returnType);
469
+ collectIfRedundant(node.returnType, assertionType, node, node.returnType);
442
470
  },
443
471
  MethodDefinition(node) {
444
472
  if (!node.value.returnType)
@@ -446,7 +474,7 @@ exports.noRedundantAnnotationAssertion = (0, createRule_1.createRule)({
446
474
  const assertionType = getReturnAssertion(node);
447
475
  if (!assertionType)
448
476
  return;
449
- reportIfRedundant(node.value.returnType, assertionType, node.key, node.value.returnType);
477
+ collectIfRedundant(node.value.returnType, assertionType, node.key, node.value.returnType);
450
478
  },
451
479
  };
452
480
  },
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noRedundantParamTypes = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
5
  const createRule_1 = require("../utils/createRule");
6
+ const disableDirectives_1 = require("../utils/disableDirectives");
6
7
  const importRemoval_1 = require("../utils/importRemoval");
7
8
  /**
8
9
  * The annotation a parameter carries, or `undefined` when it has none. A
@@ -68,57 +69,88 @@ exports.noRedundantParamTypes = (0, createRule_1.createRule)({
68
69
  create(context) {
69
70
  const sourceCode = context.getSourceCode();
70
71
  /**
71
- * Reports a parameter annotation, taking with it any import it was the only
72
+ * An annotation is stripped together with any import it was the only
72
73
  * consumer of. The two are one fix: applying either half alone leaves the
73
74
  * file worse than applying neither — a stripped annotation with its import
74
75
  * left behind fails `no-unused-vars`, and since this rule's own report is
75
76
  * resolved by the fix, nothing re-reports the debt.
76
77
  *
77
- * Orphanhood is judged against this one annotation's own removal and the
78
- * file as it stands, never against what the rest of the `--fix` run might
79
- * also delete. A sibling annotation naming the same type may be
80
- * `eslint-disable`d which a rule cannot see, since suppression is applied
81
- * to reports after they are emitted so an edit that assumes its sibling
82
- * will also go deletes an import the surviving annotation still references,
83
- * trading an unused import for a dangling type. Judging one edit at a time
84
- * is suppression-safe by construction: a suppressed report's fix never
85
- * applies, so it can never have been depended on.
78
+ * Reporting is therefore deferred to `Program:exit`: an import is unbound
79
+ * only once no reference to it survives the fix, and a file where two
80
+ * annotations name the same imported type strips both in a single pass.
81
+ * Judging each removal alone sees the sibling annotation still standing,
82
+ * concludes the binding is alive, and leaves the import stranded with no
83
+ * later pass to notice.
84
+ */
85
+ const sites = [];
86
+ /**
87
+ * Suppression is applied to reports after a rule emits them, so a suppressed
88
+ * site keeps its annotation while losing its fix. Counting its removal
89
+ * toward orphanhood would unbind an import the surviving text still spells,
90
+ * trading an unused import for a dangling type.
91
+ */
92
+ const isReportSuppressed = (0, disableDirectives_1.createSuppressionChecker)(context);
93
+ /**
94
+ * The strips that actually ship. A site is excluded when its report will be
95
+ * suppressed, or when its own removal orphans something the helper cannot
96
+ * rewrite — a local alias, an interface, a type parameter. Deleting a
97
+ * declaration is a materially riskier edit than dropping an import
98
+ * specifier, and the author is better placed to decide whether the type
99
+ * should go or be used elsewhere.
86
100
  *
87
- * The cost is that a type shared by several strippable annotations is not
88
- * unbound in a single pass; each pass removes the annotations it can see,
89
- * and only a pass that leaves the binding with no reference at all removes
90
- * the import.
101
+ * Screening individually before batching keeps one unfixable site from
102
+ * vetoing the rest: orphanhood grows monotonically with the removed set, so
103
+ * a site that cannot be planned alone can only ever poison the batch.
91
104
  */
92
- function reportParam(param, typeAnnotation) {
93
- const removal = annotationRemovalRange(typeAnnotation, sourceCode);
94
- const importRanges = (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, [removal]);
95
- context.report({
96
- node: param,
97
- messageId: 'redundantParamType',
98
- data: {
99
- paramText: sourceCode.getText(param).replace(/\s+/g, ' ').trim(),
100
- },
101
- // No plan means no binding can be unbound safely, so the annotation
102
- // stays too: the report without a fixer is the lesser damage.
103
- ...(importRanges
104
- ? {
105
- fix: (fixer) => [
106
- fixer.removeRange([removal[0], removal[1]]),
107
- ...importRanges.map((range) => fixer.removeRange([range[0], range[1]])),
108
- ],
109
- }
110
- : {}),
111
- });
105
+ function selectFixableSites() {
106
+ return sites.filter((site) => !isReportSuppressed(site.param) &&
107
+ (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, [site.removal]) !== null);
112
108
  }
113
109
  return {
110
+ 'Program:exit'() {
111
+ if (sites.length === 0)
112
+ return;
113
+ const fixable = selectFixableSites();
114
+ const removals = fixable.map((site) => site.removal);
115
+ // One plan over every surviving strip: an import referenced solely by
116
+ // annotations that all go in this pass is orphaned by their union, even
117
+ // though no single one of them orphans it.
118
+ const importRanges = removals.length > 0
119
+ ? (0, importRemoval_1.planOrphanedImportRemoval)(sourceCode, removals)
120
+ : null;
121
+ // The whole batch ships as one fix, so no strip can land without the
122
+ // others that the import's orphanhood was judged against. The rest
123
+ // report without a fixer; the carrier's pass already resolves them.
124
+ //
125
+ // No plan at all means no binding can be unbound safely, so every
126
+ // annotation stays: reports without a fixer are the lesser damage.
127
+ const carrier = importRanges ? fixable[0] : undefined;
128
+ for (const site of sites) {
129
+ context.report({
130
+ node: site.param,
131
+ messageId: 'redundantParamType',
132
+ data: { paramText: site.paramText },
133
+ fix: site === carrier && importRanges
134
+ ? (fixer) => [
135
+ ...removals.map((range) => fixer.removeRange([range[0], range[1]])),
136
+ ...importRanges.map((range) => fixer.removeRange([range[0], range[1]])),
137
+ ]
138
+ : null,
139
+ });
140
+ }
141
+ },
114
142
  ArrowFunctionExpression(node) {
115
143
  if (!hasRedundantTypeAnnotation(node))
116
144
  return;
117
145
  node.params.forEach((param) => {
118
146
  const typeAnnotation = annotationOf(param);
119
- if (typeAnnotation) {
120
- reportParam(param, typeAnnotation);
121
- }
147
+ if (!typeAnnotation)
148
+ return;
149
+ sites.push({
150
+ param,
151
+ removal: annotationRemovalRange(typeAnnotation, sourceCode),
152
+ paramText: sourceCode.getText(param).replace(/\s+/g, ' ').trim(),
153
+ });
122
154
  });
123
155
  },
124
156
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.20.93",
3
+ "version": "1.20.95",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -1,4 +1,40 @@
1
1
  [
2
+ {
3
+ "version": "1.20.95",
4
+ "date": "2026-08-03T23:44:33.733Z",
5
+ "rules": [
6
+ {
7
+ "name": "no-redundant-param-types",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 1670
11
+ ],
12
+ "summary": "unbind the import two annotations share (closes #1670)"
13
+ }
14
+ ]
15
+ },
16
+ {
17
+ "version": "1.20.94",
18
+ "date": "2026-08-03T22:57:52.839Z",
19
+ "rules": [
20
+ {
21
+ "name": "enforce-date-ttime",
22
+ "changeType": "fix",
23
+ "issues": [
24
+ 1669
25
+ ],
26
+ "summary": "unbind the import two arguments share (closes #1669)"
27
+ },
28
+ {
29
+ "name": "no-redundant-annotation-assertion",
30
+ "changeType": "fix",
31
+ "issues": [
32
+ 1668
33
+ ],
34
+ "summary": "unbind the import two annotations share (closes #1668)"
35
+ }
36
+ ]
37
+ },
2
38
  {
3
39
  "version": "1.20.93",
4
40
  "date": "2026-08-03T22:34:05.616Z",