@blumintinc/eslint-plugin-blumint 1.20.166 → 1.20.167

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.166',
226
+ version: '1.20.167',
227
227
  },
228
228
  parseOptions: {
229
229
  ecmaVersion: 2020,
@@ -119,8 +119,8 @@ function getTypeReferenceName(node) {
119
119
  /**
120
120
  * Recursively check if a TS type node composes with the given propsTypeName
121
121
  * via Pick/Omit (at any level of intersection / Readonly wrapping, union arm,
122
- * named-alias indirection, or nested in a TSTypeLiteral property's type
123
- * annotation).
122
+ * array or tuple element, named-alias indirection, or nested in a TSTypeLiteral
123
+ * property's type annotation).
124
124
  *
125
125
  * `scope` (when supplied) is the node the alias lookup walks outward from, which
126
126
  * enables resolving a locally-declared named type alias to its definition, so
@@ -185,6 +185,46 @@ function typeNodeComposesWithProps(typeNode, propsTypeName, scope, seenAliases =
185
185
  // case, where every arm composes with the single shared child.
186
186
  return typeNode.types.some((t) => typeNodeComposesWithProps(t, propsTypeName, scope, seenAliases));
187
187
  }
188
+ case utils_1.AST_NODE_TYPES.TSArrayType: {
189
+ // A list renderer's composition lives in the ELEMENT type: a parent
190
+ // declaring `buttons: readonly ActionButtonProps[]` and spreading one
191
+ // element onto each child forwards the child's ENTIRE prop object, which
192
+ // is the same DRY guarantee a direct `Pick`/`Omit` prop gives — the
193
+ // composition is simply one level of indirection away, inside the array.
194
+ // The child's contract belongs to the element type there, so demanding a
195
+ // whole-props Pick/Omit on the parent would name a composition a list
196
+ // renderer must not have (issue #2038). This is the array analogue of the
197
+ // union-arm unwrapping issue #1343 established.
198
+ return typeNodeComposesWithProps(typeNode.elementType, propsTypeName, scope, seenAliases);
199
+ }
200
+ case utils_1.AST_NODE_TYPES.TSTupleType: {
201
+ // A tuple is a fixed-length list, and each slot is handed to a child
202
+ // exactly as an array element is, so any slot that composes composes.
203
+ return typeNode.elementTypes.some((element) => typeNodeComposesWithProps(element, propsTypeName, scope, seenAliases));
204
+ }
205
+ case utils_1.AST_NODE_TYPES.TSNamedTupleMember: {
206
+ // A tuple slot's label names the slot, never its surface.
207
+ return typeNodeComposesWithProps(typeNode.elementType, propsTypeName, scope, seenAliases);
208
+ }
209
+ case utils_1.AST_NODE_TYPES.TSOptionalType:
210
+ case utils_1.AST_NODE_TYPES.TSRestType: {
211
+ // `?` and `...` govern how many slots a tuple carries, not what a slot
212
+ // carries, so the decorated type is the surface to test.
213
+ return typeNodeComposesWithProps(typeNode.typeAnnotation, propsTypeName, scope, seenAliases);
214
+ }
215
+ case utils_1.AST_NODE_TYPES.TSTypeOperator: {
216
+ // `readonly T[]` describes the same prop surface as `T[]`: the modifier
217
+ // constrains mutation of the container, never the shape of its elements.
218
+ //
219
+ // Only `readonly` unwraps. `keyof ChildProps` is the child's KEY union — a
220
+ // set of strings that hands the child nothing — and `unique symbol` is a
221
+ // nominal token, so crediting either would let a parent that merely names
222
+ // the child's keys pass as composing with its props.
223
+ if (typeNode.operator !== 'readonly' || !typeNode.typeAnnotation) {
224
+ return false;
225
+ }
226
+ return typeNodeComposesWithProps(typeNode.typeAnnotation, propsTypeName, scope, seenAliases);
227
+ }
188
228
  case utils_1.AST_NODE_TYPES.TSTypeLiteral: {
189
229
  // Check property signatures for nested composition
190
230
  // e.g. { iconProps?: Omit<GradientIconButtonProps, 'IconComponent'> }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.20.166",
3
+ "version": "1.20.167",
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.167",
4
+ "date": "2026-08-18T04:28:02.499Z",
5
+ "rules": [
6
+ {
7
+ "name": "require-props-composition",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 2038
11
+ ],
12
+ "summary": "credit composition through an array element (closes #2038)"
13
+ }
14
+ ]
15
+ },
2
16
  {
3
17
  "version": "1.20.166",
4
18
  "date": "2026-08-18T03:18:38.188Z",