@blumintinc/eslint-plugin-blumint 1.13.0 → 1.15.0

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.
Files changed (199) hide show
  1. package/README.md +169 -139
  2. package/lib/index.js +200 -9
  3. package/lib/rules/array-methods-this-context.js +1 -1
  4. package/lib/rules/avoid-utils-directory.js +0 -4
  5. package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
  6. package/lib/rules/consistent-callback-naming.js +43 -4
  7. package/lib/rules/dynamic-https-errors.d.ts +1 -1
  8. package/lib/rules/dynamic-https-errors.js +134 -44
  9. package/lib/rules/enforce-assert-safe-object-key.js +11 -11
  10. package/lib/rules/enforce-boolean-naming-prefixes.js +11 -235
  11. package/lib/rules/enforce-callback-memo.js +1 -1
  12. package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
  13. package/lib/rules/enforce-console-error.js +35 -7
  14. package/lib/rules/enforce-date-ttime.d.ts +1 -0
  15. package/lib/rules/enforce-date-ttime.js +156 -0
  16. package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
  17. package/lib/rules/enforce-dynamic-file-naming.js +53 -28
  18. package/lib/rules/enforce-dynamic-firebase-imports.d.ts +2 -1
  19. package/lib/rules/enforce-dynamic-firebase-imports.js +3 -2
  20. package/lib/rules/enforce-early-destructuring.d.ts +2 -0
  21. package/lib/rules/enforce-early-destructuring.js +980 -0
  22. package/lib/rules/enforce-empty-object-check.d.ts +11 -0
  23. package/lib/rules/enforce-empty-object-check.js +502 -0
  24. package/lib/rules/enforce-exported-function-types.js +1 -1
  25. package/lib/rules/enforce-f-extension-for-entry-points.d.ts +8 -0
  26. package/lib/rules/enforce-f-extension-for-entry-points.js +283 -0
  27. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +100 -50
  28. package/lib/rules/enforce-firestore-doc-ref-generic.js +15 -3
  29. package/lib/rules/enforce-firestore-facade.js +4 -2
  30. package/lib/rules/enforce-firestore-set-merge.js +3 -4
  31. package/lib/rules/enforce-global-constants.js +4 -4
  32. package/lib/rules/enforce-id-capitalization.js +1 -1
  33. package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
  34. package/lib/rules/enforce-memoize-async.js +71 -21
  35. package/lib/rules/enforce-memoize-getters.js +39 -15
  36. package/lib/rules/enforce-microdiff.js +1 -1
  37. package/lib/rules/enforce-object-literal-as-const.js +4 -4
  38. package/lib/rules/enforce-positive-naming.js +4 -0
  39. package/lib/rules/enforce-props-argument-name.js +44 -18
  40. package/lib/rules/enforce-safe-stringify.js +1 -1
  41. package/lib/rules/enforce-serializable-params.js +3 -3
  42. package/lib/rules/enforce-singular-type-names.js +1 -1
  43. package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
  44. package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
  45. package/lib/rules/enforce-storage-context.d.ts +9 -0
  46. package/lib/rules/enforce-storage-context.js +646 -0
  47. package/lib/rules/enforce-timestamp-now.js +23 -7
  48. package/lib/rules/enforce-transform-memoization.d.ts +4 -0
  49. package/lib/rules/enforce-transform-memoization.js +416 -0
  50. package/lib/rules/enforce-typescript-markdown-code-blocks.js +7 -3
  51. package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
  52. package/lib/rules/enforce-unique-cursor-headers.js +365 -0
  53. package/lib/rules/enforce-verb-noun-naming.js +3818 -4644
  54. package/lib/rules/ensure-pointer-events-none.js +28 -2
  55. package/lib/rules/export-if-in-doubt.js +27 -4
  56. package/lib/rules/extract-global-constants.js +40 -13
  57. package/lib/rules/fast-deep-equal-over-microdiff.js +29 -39
  58. package/lib/rules/firestore-transaction-reads-before-writes.js +50 -4
  59. package/lib/rules/flatten-push-calls.d.ts +2 -0
  60. package/lib/rules/flatten-push-calls.js +428 -0
  61. package/lib/rules/global-const-style.js +66 -23
  62. package/lib/rules/jsdoc-above-field.d.ts +11 -0
  63. package/lib/rules/jsdoc-above-field.js +208 -0
  64. package/lib/rules/key-only-outermost-element.js +21 -3
  65. package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
  66. package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
  67. package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
  68. package/lib/rules/memo-compare-deeply-complex-props.js +801 -0
  69. package/lib/rules/memo-nested-react-components.d.ts +8 -0
  70. package/lib/rules/memo-nested-react-components.js +415 -0
  71. package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
  72. package/lib/rules/no-always-true-false-conditions.js +68 -8
  73. package/lib/rules/no-array-length-in-deps.js +9 -3
  74. package/lib/rules/no-async-foreach.js +215 -12
  75. package/lib/rules/no-circular-references.d.ts +2 -1
  76. package/lib/rules/no-circular-references.js +31 -32
  77. package/lib/rules/no-class-instance-destructuring.js +55 -12
  78. package/lib/rules/no-complex-cloud-params.js +7 -3
  79. package/lib/rules/no-compositing-layer-props.js +2 -2
  80. package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
  81. package/lib/rules/no-console-error.d.ts +9 -0
  82. package/lib/rules/no-console-error.js +527 -0
  83. package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
  84. package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
  85. package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
  86. package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
  87. package/lib/rules/no-entire-object-hook-deps.js +52 -7
  88. package/lib/rules/no-excessive-parent-chain.js +4 -1
  89. package/lib/rules/no-explicit-return-type.d.ts +2 -1
  90. package/lib/rules/no-explicit-return-type.js +215 -26
  91. package/lib/rules/no-filter-without-return.js +5 -1
  92. package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
  93. package/lib/rules/no-firestore-jest-mock.js +126 -8
  94. package/lib/rules/no-firestore-object-arrays.js +67 -12
  95. package/lib/rules/no-handler-suffix.d.ts +12 -0
  96. package/lib/rules/no-handler-suffix.js +305 -0
  97. package/lib/rules/no-hungarian.js +1 -1
  98. package/lib/rules/no-inline-component-prop.d.ts +10 -0
  99. package/lib/rules/no-inline-component-prop.js +465 -0
  100. package/lib/rules/no-jsx-in-hooks.js +6 -1
  101. package/lib/rules/no-jsx-whitespace-literal.js +8 -2
  102. package/lib/rules/no-margin-properties.js +6 -6
  103. package/lib/rules/no-memoize-on-static.js +9 -1
  104. package/lib/rules/no-misleading-boolean-prefixes.js +7 -3
  105. package/lib/rules/no-misused-switch-case.js +22 -1
  106. package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
  107. package/lib/rules/no-mixed-firestore-transactions.js +296 -34
  108. package/lib/rules/no-mock-firebase-admin.js +5 -2
  109. package/lib/rules/no-object-values-on-strings.js +10 -2
  110. package/lib/rules/no-overridable-method-calls-in-constructor.js +29 -17
  111. package/lib/rules/no-passthrough-getters.d.ts +2 -2
  112. package/lib/rules/no-passthrough-getters.js +120 -2
  113. package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
  114. package/lib/rules/no-redundant-annotation-assertion.js +402 -0
  115. package/lib/rules/no-redundant-param-types.js +16 -6
  116. package/lib/rules/no-redundant-this-params.d.ts +3 -0
  117. package/lib/rules/no-redundant-this-params.js +508 -0
  118. package/lib/rules/no-redundant-usecallback-wrapper.js +16 -3
  119. package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
  120. package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
  121. package/lib/rules/no-restricted-properties-fix.js +11 -10
  122. package/lib/rules/no-separate-loading-state.js +7 -16
  123. package/lib/rules/no-stale-state-across-await.js +1 -1
  124. package/lib/rules/no-type-assertion-returns.js +43 -49
  125. package/lib/rules/no-undefined-null-passthrough.js +61 -31
  126. package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
  127. package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
  128. package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
  129. package/lib/rules/no-unnecessary-destructuring-rename.js +344 -0
  130. package/lib/rules/no-unnecessary-destructuring.js +14 -4
  131. package/lib/rules/no-unnecessary-verb-suffix.js +2 -1
  132. package/lib/rules/no-unpinned-dependencies.js +36 -5
  133. package/lib/rules/no-unused-props.d.ts +2 -2
  134. package/lib/rules/no-unused-props.js +295 -91
  135. package/lib/rules/no-unused-usestate.js +6 -2
  136. package/lib/rules/no-useless-fragment.js +28 -2
  137. package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
  138. package/lib/rules/no-useless-usememo-primitives.js +393 -0
  139. package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
  140. package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
  141. package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
  142. package/lib/rules/omit-index-html.d.ts +2 -1
  143. package/lib/rules/omit-index-html.js +62 -7
  144. package/lib/rules/optimize-object-boolean-conditions.js +6 -4
  145. package/lib/rules/parallelize-async-operations.js +143 -59
  146. package/lib/rules/prefer-batch-operations.d.ts +1 -3
  147. package/lib/rules/prefer-batch-operations.js +32 -5
  148. package/lib/rules/prefer-block-comments-for-declarations.js +9 -4
  149. package/lib/rules/prefer-clone-deep.js +3 -3
  150. package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
  151. package/lib/rules/prefer-destructuring-no-class.js +244 -59
  152. package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
  153. package/lib/rules/prefer-docsetter-setall.js +243 -0
  154. package/lib/rules/prefer-field-paths-in-transforms.js +4 -3
  155. package/lib/rules/prefer-fragment-component.js +1 -1
  156. package/lib/rules/prefer-fragment-shorthand.js +2 -1
  157. package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
  158. package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
  159. package/lib/rules/prefer-global-router-state-key.js +88 -13
  160. package/lib/rules/prefer-memoized-props.d.ts +3 -0
  161. package/lib/rules/prefer-memoized-props.js +445 -0
  162. package/lib/rules/prefer-next-dynamic.js +60 -69
  163. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +118 -8
  164. package/lib/rules/prefer-nullish-coalescing-override.d.ts +2 -1
  165. package/lib/rules/prefer-nullish-coalescing-override.js +11 -5
  166. package/lib/rules/prefer-params-over-parent-id.js +221 -172
  167. package/lib/rules/prefer-settings-object.js +27 -10
  168. package/lib/rules/prefer-type-alias-over-typeof-constant.js +9 -0
  169. package/lib/rules/prefer-type-over-interface.js +7 -2
  170. package/lib/rules/prefer-url-tostring-over-tojson.js +6 -3
  171. package/lib/rules/prefer-use-deep-compare-memo.js +8 -5
  172. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
  173. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +1 -1
  174. package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
  175. package/lib/rules/prevent-children-clobber.d.ts +2 -0
  176. package/lib/rules/prevent-children-clobber.js +540 -0
  177. package/lib/rules/react-memoize-literals.d.ts +4 -0
  178. package/lib/rules/react-memoize-literals.js +614 -0
  179. package/lib/rules/react-usememo-should-be-component.js +2 -2
  180. package/lib/rules/require-hooks-default-params.js +17 -16
  181. package/lib/rules/require-https-error-cause.d.ts +4 -0
  182. package/lib/rules/require-https-error-cause.js +155 -0
  183. package/lib/rules/require-https-error.js +43 -21
  184. package/lib/rules/require-image-optimized.js +1 -1
  185. package/lib/rules/require-memo.js +18 -10
  186. package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
  187. package/lib/rules/require-memoize-jsx-returners.js +485 -0
  188. package/lib/rules/require-usememo-object-literals.js +2 -3
  189. package/lib/rules/test-file-location-enforcement.js +1 -1
  190. package/lib/rules/use-latest-callback.js +6 -5
  191. package/lib/rules/vertically-group-related-functions.d.ts +16 -0
  192. package/lib/rules/vertically-group-related-functions.js +480 -0
  193. package/lib/utils/ASTHelpers.d.ts +47 -2
  194. package/lib/utils/ASTHelpers.js +370 -112
  195. package/lib/utils/getMethodName.d.ts +27 -0
  196. package/lib/utils/getMethodName.js +35 -0
  197. package/lib/utils/tsTypeClassifier.d.ts +30 -0
  198. package/lib/utils/tsTypeClassifier.js +149 -0
  199. package/package.json +3 -1
@@ -0,0 +1,465 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.noInlineComponentProp = void 0;
27
+ const utils_1 = require("@typescript-eslint/utils");
28
+ const ts = __importStar(require("typescript"));
29
+ const createRule_1 = require("../utils/createRule");
30
+ const ASTHelpers_1 = require("../utils/ASTHelpers");
31
+ const DEFAULT_PROP_PATTERNS = ['CatalogWrapper', '*Wrapper', '*Component'];
32
+ const DEFAULT_OPTIONS = {
33
+ props: DEFAULT_PROP_PATTERNS,
34
+ allowRenderProps: true,
35
+ allowModuleScopeFactories: true,
36
+ };
37
+ const INLINE_COMPONENT_NAME = 'inline component';
38
+ function isPascalCase(name) {
39
+ return /^[A-Z][A-Za-z0-9]*$/.test(name);
40
+ }
41
+ function matchesPattern(name, pattern) {
42
+ if (pattern === '*')
43
+ return true;
44
+ if (pattern.includes('*')) {
45
+ const wildcardCount = (pattern.match(/\*/g) || []).length;
46
+ if (wildcardCount > 2) {
47
+ return false;
48
+ }
49
+ const escaped = pattern
50
+ .replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&')
51
+ .replace(/\*/g, '.*');
52
+ return new RegExp(`^${escaped}$`).test(name);
53
+ }
54
+ return name === pattern;
55
+ }
56
+ function isRenderPropName(name) {
57
+ if (name === 'children' || name === 'child' || name === 'render') {
58
+ return true;
59
+ }
60
+ if (name.startsWith('render')) {
61
+ return true;
62
+ }
63
+ return (name === 'rowRenderer' ||
64
+ name === 'cellRenderer' ||
65
+ name === 'itemRenderer' ||
66
+ name === 'renderItem' ||
67
+ name === 'renderRow');
68
+ }
69
+ function unwrapExpression(expr) {
70
+ let current = expr;
71
+ while (current) {
72
+ if (current.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
73
+ current.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression) {
74
+ current = current.expression;
75
+ continue;
76
+ }
77
+ if (current.type === utils_1.AST_NODE_TYPES.ChainExpression) {
78
+ current = current.expression;
79
+ continue;
80
+ }
81
+ break;
82
+ }
83
+ return current ?? null;
84
+ }
85
+ function isReactCreateElementCall(expr) {
86
+ if (expr.type !== utils_1.AST_NODE_TYPES.CallExpression)
87
+ return false;
88
+ const callee = expr.callee;
89
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
90
+ callee.name === 'createElement') {
91
+ return true;
92
+ }
93
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
94
+ !callee.computed &&
95
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
96
+ callee.property.name === 'createElement') {
97
+ if (callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
98
+ callee.object.name === 'React') {
99
+ return true;
100
+ }
101
+ }
102
+ return false;
103
+ }
104
+ function returnsCreateElement(node) {
105
+ if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
106
+ isReactCreateElementCall(node)) {
107
+ return true;
108
+ }
109
+ if (node.type === utils_1.AST_NODE_TYPES.BlockStatement) {
110
+ for (const statement of node.body) {
111
+ if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
112
+ statement.argument &&
113
+ statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression &&
114
+ isReactCreateElementCall(statement.argument)) {
115
+ return true;
116
+ }
117
+ }
118
+ }
119
+ return false;
120
+ }
121
+ function isFunctionNode(node) {
122
+ if (!node)
123
+ return false;
124
+ return (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
125
+ node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
126
+ node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration);
127
+ }
128
+ function getCalleeName(callee) {
129
+ const unwrapped = unwrapExpression(callee);
130
+ if (!unwrapped)
131
+ return null;
132
+ if (unwrapped.type === utils_1.AST_NODE_TYPES.Identifier) {
133
+ return unwrapped.name;
134
+ }
135
+ if (unwrapped.type === utils_1.AST_NODE_TYPES.MemberExpression &&
136
+ !unwrapped.computed &&
137
+ unwrapped.property.type === utils_1.AST_NODE_TYPES.Identifier) {
138
+ const prop = unwrapped.property.name;
139
+ if (unwrapped.object.type === utils_1.AST_NODE_TYPES.Identifier &&
140
+ unwrapped.object.name) {
141
+ return `${unwrapped.object.name}.${prop}`;
142
+ }
143
+ return prop;
144
+ }
145
+ return null;
146
+ }
147
+ function getFunctionFromCall(call) {
148
+ const calleeName = getCalleeName(call.callee);
149
+ const firstArg = unwrapExpression(call.arguments[0] ?? null);
150
+ if (!firstArg || !isFunctionNode(firstArg)) {
151
+ return undefined;
152
+ }
153
+ if (calleeName === 'useCallback' ||
154
+ calleeName === 'React.useCallback' ||
155
+ calleeName === 'useMemo' ||
156
+ calleeName === 'React.useMemo' ||
157
+ calleeName === 'memo' ||
158
+ calleeName === 'React.memo' ||
159
+ calleeName === 'forwardRef' ||
160
+ calleeName === 'React.forwardRef') {
161
+ return firstArg;
162
+ }
163
+ return undefined;
164
+ }
165
+ function getFunctionFromInit(init) {
166
+ const unwrapped = unwrapExpression(init);
167
+ if (!unwrapped)
168
+ return undefined;
169
+ if (isFunctionNode(unwrapped)) {
170
+ return unwrapped;
171
+ }
172
+ if (unwrapped.type === utils_1.AST_NODE_TYPES.CallExpression) {
173
+ return getFunctionFromCall(unwrapped);
174
+ }
175
+ return undefined;
176
+ }
177
+ function isInModuleScope(node) {
178
+ let current = node.parent;
179
+ while (current) {
180
+ if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
181
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
182
+ current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
183
+ current.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
184
+ return false;
185
+ }
186
+ if (current.type === utils_1.AST_NODE_TYPES.Program) {
187
+ return true;
188
+ }
189
+ current = current.parent;
190
+ }
191
+ return true;
192
+ }
193
+ function isComponentLikeFunction(fn, context, displayName) {
194
+ const body = fn.body;
195
+ const hasJSX = !!body && ASTHelpers_1.ASTHelpers.returnsJSX(body, context);
196
+ const expressionBody = fn.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
197
+ fn.body.type !== utils_1.AST_NODE_TYPES.BlockStatement
198
+ ? fn.body
199
+ : null;
200
+ const hasCreateElement = (expressionBody && isReactCreateElementCall(expressionBody)) ||
201
+ (body ? returnsCreateElement(body) : false);
202
+ const looksLikeComponent = displayName ? isPascalCase(displayName) : false;
203
+ return hasJSX || hasCreateElement || looksLikeComponent;
204
+ }
205
+ function findVariableInScopes(context, identifier) {
206
+ const sourceCode = context.sourceCode;
207
+ let scope = null;
208
+ try {
209
+ // Tolerate ESLint API variations across versions/configurations.
210
+ // Some contexts may not provide getScope or may throw unexpectedly.
211
+ scope = sourceCode.getScope?.(identifier) ?? context.getScope();
212
+ }
213
+ catch {
214
+ scope = null;
215
+ }
216
+ while (scope) {
217
+ const variable = scope.variables.find((v) => v.name === identifier.name);
218
+ if (variable)
219
+ return variable;
220
+ scope = scope.upper;
221
+ }
222
+ return undefined;
223
+ }
224
+ function findObjectPropertyFunction(objExpr, propertyName) {
225
+ for (const prop of objExpr.properties) {
226
+ if (prop.type !== utils_1.AST_NODE_TYPES.Property)
227
+ continue;
228
+ if (prop.computed)
229
+ continue;
230
+ if (prop.key.type === utils_1.AST_NODE_TYPES.Identifier) {
231
+ if (prop.key.name !== propertyName)
232
+ continue;
233
+ }
234
+ else if (prop.key.type === utils_1.AST_NODE_TYPES.Literal &&
235
+ prop.key.value === propertyName) {
236
+ // match
237
+ }
238
+ else {
239
+ continue;
240
+ }
241
+ const value = unwrapExpression(prop.value);
242
+ if (value && isFunctionNode(value)) {
243
+ return value;
244
+ }
245
+ }
246
+ return undefined;
247
+ }
248
+ exports.noInlineComponentProp = (0, createRule_1.createRule)({
249
+ name: 'no-inline-component-prop',
250
+ meta: {
251
+ type: 'suggestion',
252
+ docs: {
253
+ description: 'Prevent inline function components defined in render from being passed to component-type props like CatalogWrapper to avoid remounts and UI flashes.',
254
+ recommended: 'error',
255
+ },
256
+ fixable: undefined,
257
+ schema: [
258
+ {
259
+ type: 'object',
260
+ properties: {
261
+ props: {
262
+ type: 'array',
263
+ items: { type: 'string' },
264
+ default: DEFAULT_PROP_PATTERNS,
265
+ },
266
+ allowRenderProps: {
267
+ type: 'boolean',
268
+ default: true,
269
+ },
270
+ allowModuleScopeFactories: {
271
+ type: 'boolean',
272
+ default: true,
273
+ },
274
+ },
275
+ additionalProperties: false,
276
+ },
277
+ ],
278
+ messages: {
279
+ inlineComponentProp: 'Inline component "{{componentName}}" is created inside a render scope and passed to component-type prop "{{propName}}". React treats it as a new component whenever the scope re-runs, remounting its subtree and causing UI flashes. Move the wrapper to module scope (optionally memoize with React.memo) and pass changing data via props or context instead.',
280
+ },
281
+ },
282
+ defaultOptions: [DEFAULT_OPTIONS],
283
+ create(context, [options]) {
284
+ const resolvedOptions = { ...DEFAULT_OPTIONS, ...(options ?? {}) };
285
+ const propPatterns = resolvedOptions.props ?? DEFAULT_PROP_PATTERNS;
286
+ const parserServices = context.parserServices;
287
+ const checker = parserServices?.program?.getTypeChecker();
288
+ const esTreeNodeToTSNodeMap = parserServices?.esTreeNodeToTSNodeMap;
289
+ function isComponentTypeByTypeInfo(attribute) {
290
+ if (!checker || !esTreeNodeToTSNodeMap)
291
+ return false;
292
+ const tsNode = esTreeNodeToTSNodeMap.get(attribute);
293
+ if (!tsNode)
294
+ return false;
295
+ let type;
296
+ if (ts.isExpression(tsNode)) {
297
+ type =
298
+ checker.getContextualType(tsNode) ??
299
+ checker.getTypeAtLocation(tsNode);
300
+ }
301
+ else {
302
+ type = checker.getTypeAtLocation(tsNode);
303
+ }
304
+ if (!type)
305
+ return false;
306
+ const typeText = checker.typeToString(type);
307
+ return (typeText.includes('ComponentType') ||
308
+ typeText.includes('FunctionComponent') ||
309
+ typeText.includes('ReactElement') ||
310
+ typeText.includes('FC'));
311
+ }
312
+ function isTargetProp(attribute, name) {
313
+ if (resolvedOptions.allowRenderProps && isRenderPropName(name)) {
314
+ return false;
315
+ }
316
+ const patternMatch = propPatterns.some((pattern) => matchesPattern(name, pattern));
317
+ const looksComponent = isPascalCase(name) ||
318
+ name.endsWith('Wrapper') ||
319
+ name.endsWith('Component');
320
+ if (patternMatch) {
321
+ return true;
322
+ }
323
+ if (looksComponent) {
324
+ return isComponentTypeByTypeInfo(attribute);
325
+ }
326
+ return false;
327
+ }
328
+ function shouldReportDefinition(definition, displayName) {
329
+ if (definition.type === 'ImportBinding' ||
330
+ definition.type === 'Parameter' ||
331
+ definition.type === 'Type') {
332
+ return false;
333
+ }
334
+ const defNode = definition.node;
335
+ const moduleScoped = isInModuleScope(defNode);
336
+ if (moduleScoped && resolvedOptions.allowModuleScopeFactories) {
337
+ return false;
338
+ }
339
+ let fnNode;
340
+ if (defNode.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
341
+ fnNode = defNode;
342
+ }
343
+ else if (defNode.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
344
+ fnNode = getFunctionFromInit(defNode.init);
345
+ }
346
+ else {
347
+ return false;
348
+ }
349
+ if (!fnNode)
350
+ return false;
351
+ return isComponentLikeFunction(fnNode, context, displayName);
352
+ }
353
+ function report(node, propName, componentName) {
354
+ context.report({
355
+ node,
356
+ messageId: 'inlineComponentProp',
357
+ data: { propName, componentName },
358
+ });
359
+ }
360
+ function handleIdentifierExpression(identifier, propName) {
361
+ const variable = findVariableInScopes(context, identifier);
362
+ if (!variable)
363
+ return;
364
+ const definition = variable.defs.find((def) => (def.node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
365
+ def.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) &&
366
+ def.type !== 'Parameter' &&
367
+ def.type !== 'ImportBinding');
368
+ if (!definition)
369
+ return;
370
+ if (shouldReportDefinition(definition, identifier.name)) {
371
+ report(identifier, propName, identifier.name);
372
+ }
373
+ }
374
+ function handleMemberExpression(member, propName) {
375
+ if (member.computed)
376
+ return;
377
+ if (member.property.type !== utils_1.AST_NODE_TYPES.Identifier)
378
+ return;
379
+ const objectId = member.object;
380
+ if (objectId.type !== utils_1.AST_NODE_TYPES.Identifier)
381
+ return;
382
+ const variable = findVariableInScopes(context, objectId);
383
+ if (!variable)
384
+ return;
385
+ const definition = variable.defs.find((def) => def.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
386
+ def.node.id.type === utils_1.AST_NODE_TYPES.Identifier &&
387
+ def.node.id.name === objectId.name &&
388
+ def.type !== 'ImportBinding' &&
389
+ def.type !== 'Parameter' &&
390
+ def.type !== 'Type');
391
+ if (!definition)
392
+ return;
393
+ const defNode = definition.node;
394
+ if (!defNode ||
395
+ defNode.type !== utils_1.AST_NODE_TYPES.VariableDeclarator ||
396
+ !defNode.init ||
397
+ defNode.init.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
398
+ return;
399
+ }
400
+ if (isInModuleScope(defNode) &&
401
+ resolvedOptions.allowModuleScopeFactories) {
402
+ return;
403
+ }
404
+ const fnNode = findObjectPropertyFunction(defNode.init, member.property.name);
405
+ if (fnNode &&
406
+ isComponentLikeFunction(fnNode, context, member.property.name)) {
407
+ report(member, propName, member.property.name);
408
+ }
409
+ }
410
+ function handleInlineFunctionExpression(fn, propName) {
411
+ const explicitName = fn.type === utils_1.AST_NODE_TYPES.FunctionExpression ? fn.id?.name : undefined;
412
+ if (!isComponentLikeFunction(fn, context, explicitName)) {
413
+ return;
414
+ }
415
+ const displayName = (fn.type === utils_1.AST_NODE_TYPES.FunctionExpression && fn.id?.name) ||
416
+ INLINE_COMPONENT_NAME;
417
+ report(fn, propName, displayName);
418
+ }
419
+ function handleCallExpression(call, propName) {
420
+ const fnNode = getFunctionFromCall(call);
421
+ if (!fnNode)
422
+ return;
423
+ if (isComponentLikeFunction(fnNode, context)) {
424
+ report(call, propName, INLINE_COMPONENT_NAME);
425
+ }
426
+ }
427
+ return {
428
+ JSXAttribute(node) {
429
+ if (!node.name || node.name.type !== utils_1.AST_NODE_TYPES.JSXIdentifier) {
430
+ return;
431
+ }
432
+ const propName = node.name.name;
433
+ if (!isTargetProp(node, propName))
434
+ return;
435
+ if (!node.value ||
436
+ node.value.type !== utils_1.AST_NODE_TYPES.JSXExpressionContainer) {
437
+ return;
438
+ }
439
+ if (node.value.expression.type === utils_1.AST_NODE_TYPES.JSXEmptyExpression) {
440
+ return;
441
+ }
442
+ const expression = unwrapExpression(node.value.expression);
443
+ if (!expression)
444
+ return;
445
+ if (expression.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
446
+ expression.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
447
+ handleInlineFunctionExpression(expression, propName);
448
+ return;
449
+ }
450
+ if (expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
451
+ handleCallExpression(expression, propName);
452
+ return;
453
+ }
454
+ if (expression.type === utils_1.AST_NODE_TYPES.Identifier) {
455
+ handleIdentifierExpression(expression, propName);
456
+ return;
457
+ }
458
+ if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression) {
459
+ handleMemberExpression(expression, propName);
460
+ }
461
+ },
462
+ };
463
+ },
464
+ });
465
+ //# sourceMappingURL=no-inline-component-prop.js.map
@@ -135,7 +135,7 @@ exports.noJsxInHooks = (0, createRule_1.createRule)({
135
135
  },
136
136
  schema: [],
137
137
  messages: {
138
- noJsxInHooks: 'Hooks should not return JSX. Convert this hook into a component or extract the JSX into a separate component.',
138
+ noJsxInHooks: 'Hook "{{hookName}}" returns JSX. Hooks are meant to expose data and behavior; embedding JSX turns the hook into a hidden component and forces callers to use a hook where a component should render. Move the JSX into a component and have the hook return the values and callbacks that component needs instead.',
139
139
  },
140
140
  },
141
141
  defaultOptions: [],
@@ -148,6 +148,7 @@ exports.noJsxInHooks = (0, createRule_1.createRule)({
148
148
  context.report({
149
149
  node: node.id,
150
150
  messageId: 'noJsxInHooks',
151
+ data: { hookName: node.id.name },
151
152
  });
152
153
  return;
153
154
  }
@@ -157,6 +158,7 @@ exports.noJsxInHooks = (0, createRule_1.createRule)({
157
158
  context.report({
158
159
  node: node.id,
159
160
  messageId: 'noJsxInHooks',
161
+ data: { hookName: node.id.name },
160
162
  });
161
163
  }
162
164
  }
@@ -173,6 +175,7 @@ exports.noJsxInHooks = (0, createRule_1.createRule)({
173
175
  context.report({
174
176
  node: parent.id,
175
177
  messageId: 'noJsxInHooks',
178
+ data: { hookName: parent.id.name },
176
179
  });
177
180
  return;
178
181
  }
@@ -181,6 +184,7 @@ exports.noJsxInHooks = (0, createRule_1.createRule)({
181
184
  context.report({
182
185
  node: parent.id,
183
186
  messageId: 'noJsxInHooks',
187
+ data: { hookName: parent.id.name },
184
188
  });
185
189
  return;
186
190
  }
@@ -190,6 +194,7 @@ exports.noJsxInHooks = (0, createRule_1.createRule)({
190
194
  context.report({
191
195
  node: parent.id,
192
196
  messageId: 'noJsxInHooks',
197
+ data: { hookName: parent.id.name },
193
198
  });
194
199
  }
195
200
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noJsxWhitespaceLiteral = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
4
5
  const createRule_1 = require("../utils/createRule");
5
6
  exports.noJsxWhitespaceLiteral = (0, createRule_1.createRule)({
6
7
  name: 'no-jsx-whitespace-literal',
@@ -12,19 +13,24 @@ exports.noJsxWhitespaceLiteral = (0, createRule_1.createRule)({
12
13
  },
13
14
  schema: [],
14
15
  messages: {
15
- noWhitespaceLiteral: 'Avoid using {" "} for spacing in JSX. Use proper text nodes or CSS spacing instead.',
16
+ noWhitespaceLiteral: 'Whitespace-only JSX expression {{literal}} inserts fragile spacer nodes React treats the whitespace as a separate text child that shifts, disappears, or duplicates when child elements are reordered, translated, or dynamically rendered → Place spacing inside text content (e.g., "Hello ") or use CSS spacing such as gap, margin, or padding.',
16
17
  },
17
18
  },
18
19
  defaultOptions: [],
19
20
  create(context) {
21
+ const typedContext = context;
22
+ const sourceCode = typedContext.sourceCode ?? context.getSourceCode();
20
23
  return {
21
24
  JSXExpressionContainer(node) {
22
- if (node.expression.type === 'Literal' &&
25
+ if (node.expression.type === utils_1.AST_NODE_TYPES.Literal &&
23
26
  typeof node.expression.value === 'string' &&
24
27
  node.expression.value.trim() === '') {
25
28
  context.report({
26
29
  node,
27
30
  messageId: 'noWhitespaceLiteral',
31
+ data: {
32
+ literal: sourceCode.getText(node),
33
+ },
28
34
  });
29
35
  }
30
36
  },
@@ -39,7 +39,7 @@ exports.noMarginProperties = (0, createRule_1.createRule)({
39
39
  meta: {
40
40
  type: 'suggestion',
41
41
  docs: {
42
- description: 'Discourage using margin properties (margin, marginLeft, marginRight, marginTop, marginBottom, mx, my, etc.) for spacing in MUI components. Instead, prefer defining spacing with padding, gap, or the spacing prop for more predictable layouts.',
42
+ description: 'Prevent margin properties (margin, marginLeft, marginRight, marginTop, marginBottom, mx, my, etc.) in MUI styling because margins fight container-controlled spacing, double gutters, and misaligned breakpoints; keep spacing centralized with padding, gap, or spacing props instead.',
43
43
  recommended: 'warn',
44
44
  },
45
45
  schema: [
@@ -55,7 +55,7 @@ exports.noMarginProperties = (0, createRule_1.createRule)({
55
55
  },
56
56
  ],
57
57
  messages: {
58
- noMarginProperties: 'Avoid using {{property}} for spacing in MUI components. Use padding, gap, or the spacing prop instead for more predictable layouts. See https://www.youtube.com/watch?v=KVQMoEFUee8 for more details.',
58
+ noMarginProperties: 'Margin property "{{property}}" in MUI styling fights container-controlled spacing (Stack/Grid spacing, gap, responsive gutters) and produces double gutters, misalignment, and overflow as layouts shift. Keep spacing inside the component with padding or let the parent handle separation via gap/spacing so layout remains predictable.',
59
59
  },
60
60
  },
61
61
  defaultOptions: [{ autofix: false }],
@@ -233,7 +233,7 @@ exports.noMarginProperties = (0, createRule_1.createRule)({
233
233
  if (node.init?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
234
234
  node.id.type === utils_1.AST_NODE_TYPES.Identifier) {
235
235
  const variableName = node.id.name;
236
- const sourceCode = context.getSourceCode().getText();
236
+ const sourceText = context.sourceCode.getText();
237
237
  // Check for margin properties in the object
238
238
  node.init.properties.forEach((prop) => {
239
239
  if (prop.type === utils_1.AST_NODE_TYPES.Property) {
@@ -246,9 +246,9 @@ exports.noMarginProperties = (0, createRule_1.createRule)({
246
246
  }
247
247
  if (propertyName && checkProperty(propertyName)) {
248
248
  // Check if this variable is used in an sx prop
249
- if (sourceCode.includes(`sx={${variableName}}`) ||
250
- sourceCode.includes(`sx={{ ...${variableName}`) ||
251
- sourceCode.includes(`sx={Object.assign({}, ${variableName}`)) {
249
+ if (sourceText.includes(`sx={${variableName}}`) ||
250
+ sourceText.includes(`sx={{ ...${variableName}`) ||
251
+ sourceText.includes(`sx={Object.assign({}, ${variableName}`)) {
252
252
  context.report({
253
253
  node: prop,
254
254
  messageId: 'noMarginProperties',
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noMemoizeOnStatic = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
5
  const createRule_1 = require("../utils/createRule");
6
+ const getMethodName_1 = require("../utils/getMethodName");
6
7
  exports.noMemoizeOnStatic = (0, createRule_1.createRule)({
7
8
  name: 'no-memoize-on-static',
8
9
  meta: {
@@ -13,11 +14,12 @@ exports.noMemoizeOnStatic = (0, createRule_1.createRule)({
13
14
  },
14
15
  schema: [],
15
16
  messages: {
16
- noMemoizeOnStatic: '@Memoize() decorator should not be used on static methods',
17
+ noMemoizeOnStatic: 'Static member "{{methodName}}" uses @Memoize(), which shares one cache across every caller instead of per instance. Static memoization can leak stale or cross-tenant data because the cache survives across requests for the lifetime of the process. Remove @Memoize() or move the logic into an instance method so each consumer gets its own cache lifecycle.',
17
18
  },
18
19
  },
19
20
  defaultOptions: [],
20
21
  create(context) {
22
+ const sourceCode = context.getSourceCode();
21
23
  // Track renamed imports of Memoize
22
24
  const memoizeAliases = new Set(['Memoize']);
23
25
  return {
@@ -43,6 +45,12 @@ exports.noMemoizeOnStatic = (0, createRule_1.createRule)({
43
45
  context.report({
44
46
  node: decorator,
45
47
  messageId: 'noMemoizeOnStatic',
48
+ data: {
49
+ methodName: (0, getMethodName_1.getMethodName)(node, sourceCode, {
50
+ privateIdentifierPrefix: '#',
51
+ computedFallbackToText: false,
52
+ }) || '<unknown>',
53
+ },
46
54
  });
47
55
  }
48
56
  }
@@ -210,7 +210,7 @@ exports.noMisleadingBooleanPrefixes = (0, createRule_1.createRule)({
210
210
  meta: {
211
211
  type: 'problem',
212
212
  docs: {
213
- description: 'Reserve boolean-style prefixes (is/has/should) for functions that actually return boolean values.',
213
+ description: 'Reserve boolean-style prefixes (is/has/should) for functions that actually return boolean values to avoid misleading call sites.',
214
214
  recommended: 'error',
215
215
  },
216
216
  schema: [
@@ -226,7 +226,7 @@ exports.noMisleadingBooleanPrefixes = (0, createRule_1.createRule)({
226
226
  },
227
227
  ],
228
228
  messages: {
229
- nonBooleanReturn: 'Function "{{name}}" uses a boolean-style prefix but does not return a boolean value.',
229
+ nonBooleanReturn: 'Function "{{name}}" uses a boolean-style prefix but its return value is not guaranteed to be boolean. Boolean prefixes promise a yes/no answer; returning strings, objects, or void misleads callers and hides incorrect branching. Return a real boolean (e.g., add an explicit comparison or Boolean(...)) or rename the function to drop the boolean-style prefix (prefixes treated as boolean: {{prefixes}}).',
230
230
  },
231
231
  },
232
232
  defaultOptions: [{ prefixes: DEFAULT_PREFIXES }],
@@ -236,7 +236,11 @@ exports.noMisleadingBooleanPrefixes = (0, createRule_1.createRule)({
236
236
  return startsWithBooleanPrefix(name, prefixes);
237
237
  }
238
238
  function report(node, name) {
239
- context.report({ node, messageId: 'nonBooleanReturn', data: { name } });
239
+ context.report({
240
+ node,
241
+ messageId: 'nonBooleanReturn',
242
+ data: { name, prefixes: prefixes.join(', ') },
243
+ });
240
244
  }
241
245
  function checkFunctionLike(node, name, reportNode) {
242
246
  if (!shouldCheckName(name))