@blumintinc/eslint-plugin-blumint 1.21.10 → 1.21.11

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
@@ -224,7 +224,7 @@ function noFrontendImportsFromFunctionsPatterns(pattern) {
224
224
  module.exports = {
225
225
  meta: {
226
226
  name: '@blumintinc/eslint-plugin-blumint',
227
- version: '1.21.10',
227
+ version: '1.21.11',
228
228
  },
229
229
  parseOptions: {
230
230
  ecmaVersion: 2020,
@@ -350,9 +350,14 @@ const storageContainerOf = (node) => {
350
350
  * the file compiling is not.
351
351
  *
352
352
  * A reference STORED INTO a composite literal is followed through that
353
- * container, since storing does not copy — see `storageContainerOf`. A
354
- * destructuring id extracts a member rather than the whole, so it is not an
355
- * alias here.
353
+ * container, since storing does not copy — see `storageContainerOf`.
354
+ *
355
+ * A destructuring id is accepted in BOTH spellings. It does not name the whole
356
+ * value, but every binding it introduces is typed from that value, and a rest
357
+ * element is itself a fresh container the assertion narrows: `const [, ...rest]
358
+ * = ITEMS` gives `rest` the frozen element type, so `rest.push(4)` is TS2345
359
+ * for an input that compiled. Admitting only the object spelling gave the same
360
+ * construct opposite verdicts (#2336).
356
361
  */
357
362
  const aliasDeclaratorOf = (identifier) => {
358
363
  // Ascends strictly, so reaching a node with no parent terminates the walk.
@@ -362,7 +367,8 @@ const aliasDeclaratorOf = (identifier) => {
362
367
  if (declarator?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
363
368
  declarator.init === value &&
364
369
  (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier ||
365
- declarator.id.type === utils_1.AST_NODE_TYPES.ObjectPattern)) {
370
+ declarator.id.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
371
+ declarator.id.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
366
372
  return declarator;
367
373
  }
368
374
  const container = storageContainerOf(value);
@@ -410,13 +410,16 @@ const consumptionOfReference = (identifier, reactImports) => {
410
410
  * fragment. Falling back to the name there keeps the rule's reach while letting
411
411
  * evidence override the guess wherever evidence exists.
412
412
  */
413
- const consumptionOfBinding = (node, context, reactImports) => {
414
- const declarator = parentBeyondChain(node);
415
- if (declarator?.type !== utils_1.AST_NODE_TYPES.VariableDeclarator) {
416
- return 'unknown';
417
- }
413
+ const consumptionOfDeclaration = (declaration, id, context, reactImports) => {
414
+ // `getDeclaredVariables` on a FunctionDeclaration yields its PARAMETERS
415
+ // alongside the function name, and a parameter handed to a non-component prop
416
+ // votes `callback` for a binding it says nothing about. Keep only the
417
+ // variable this declaration's own id introduces.
418
+ const declared = context
419
+ .getDeclaredVariables(declaration)
420
+ .filter((variable) => variable.defs.some((def) => def.name === id));
418
421
  let sawCallback = false;
419
- for (const variable of context.getDeclaredVariables(declarator)) {
422
+ for (const variable of declared) {
420
423
  for (const reference of variable.references) {
421
424
  const consumption = consumptionOfReference(reference.identifier, reactImports);
422
425
  // A single mounting use settles it: the identity churn happens there
@@ -431,6 +434,14 @@ const consumptionOfBinding = (node, context, reactImports) => {
431
434
  }
432
435
  return sawCallback ? 'callback' : 'unknown';
433
436
  };
437
+ const consumptionOfBinding = (node, context, reactImports) => {
438
+ const declarator = parentBeyondChain(node);
439
+ if (declarator?.type !== utils_1.AST_NODE_TYPES.VariableDeclarator ||
440
+ declarator.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
441
+ return 'unknown';
442
+ }
443
+ return consumptionOfDeclaration(declarator, declarator.id, context, reactImports);
444
+ };
434
445
  /**
435
446
  * The values a container hands to its caller: object property values and array
436
447
  * elements. Mirrors `containedValues` in the paired `require-memo` rule (#1919),
@@ -841,8 +852,10 @@ See: https://react.dev/learn/your-first-component#nesting-and-organizing-compone
841
852
  return;
842
853
  if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier)
843
854
  return;
844
- // Only check if name starts with uppercase (convention for components)
845
- if (!isPascalCaseName(node.id.name))
855
+ const vdConsumption = consumptionOfDeclaration(node, node.id, context, reactImports);
856
+ if (vdConsumption === 'callback')
857
+ return;
858
+ if (vdConsumption === 'unknown' && !isPascalCaseName(node.id.name))
846
859
  return;
847
860
  if (!isInsideFunction(node))
848
861
  return;
@@ -866,7 +879,12 @@ See: https://react.dev/learn/your-first-component#nesting-and-organizing-compone
866
879
  reportNestedComponentViolation(node, node.id.name, 'a render body');
867
880
  },
868
881
  FunctionDeclaration(node) {
869
- if (!node.id || !isPascalCaseName(node.id.name))
882
+ if (!node.id)
883
+ return;
884
+ const fdConsumption = consumptionOfDeclaration(node, node.id, context, reactImports);
885
+ if (fdConsumption === 'callback')
886
+ return;
887
+ if (fdConsumption === 'unknown' && !isPascalCaseName(node.id.name))
870
888
  return;
871
889
  if (!isInsideFunction(node))
872
890
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.21.10",
3
+ "version": "1.21.11",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -1,4 +1,26 @@
1
1
  [
2
+ {
3
+ "version": "1.21.11",
4
+ "date": "2026-09-05T11:06:01.167Z",
5
+ "rules": [
6
+ {
7
+ "name": "global-const-style",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 2336
11
+ ],
12
+ "summary": "follow the array spelling of a destructured copy, and the rest element that needs no copy (closes #2336)"
13
+ },
14
+ {
15
+ "name": "memo-nested-react-components",
16
+ "changeType": "fix",
17
+ "issues": [
18
+ 2335
19
+ ],
20
+ "summary": "decide by the use site in the two non-hook visitors too (closes #2335)"
21
+ }
22
+ ]
23
+ },
2
24
  {
3
25
  "version": "1.21.10",
4
26
  "date": "2026-09-05T09:28:38.523Z",