@blumintinc/eslint-plugin-blumint 1.12.6 → 1.14.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 (258) hide show
  1. package/README.md +171 -99
  2. package/lib/index.js +300 -17
  3. package/lib/rules/array-methods-this-context.js +20 -2
  4. package/lib/rules/avoid-utils-directory.js +11 -2
  5. package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
  6. package/lib/rules/consistent-callback-naming.js +1 -1
  7. package/lib/rules/dynamic-https-errors.d.ts +3 -1
  8. package/lib/rules/dynamic-https-errors.js +48 -11
  9. package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
  10. package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +52 -96
  11. package/lib/rules/enforce-assert-throws.d.ts +3 -1
  12. package/lib/rules/enforce-assert-throws.js +493 -51
  13. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
  14. package/lib/rules/enforce-boolean-naming-prefixes.js +820 -39
  15. package/lib/rules/enforce-callable-types.js +4 -4
  16. package/lib/rules/enforce-callback-memo.js +143 -18
  17. package/lib/rules/enforce-centralized-mock-firestore.js +5 -2
  18. package/lib/rules/enforce-console-error.d.ts +3 -0
  19. package/lib/rules/enforce-console-error.js +278 -0
  20. package/lib/rules/enforce-css-media-queries.js +24 -46
  21. package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
  22. package/lib/rules/enforce-dynamic-file-naming.js +53 -28
  23. package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
  24. package/lib/rules/enforce-dynamic-imports.js +1 -1
  25. package/lib/rules/enforce-early-destructuring.d.ts +2 -0
  26. package/lib/rules/enforce-early-destructuring.js +980 -0
  27. package/lib/rules/enforce-empty-object-check.d.ts +11 -0
  28. package/lib/rules/enforce-empty-object-check.js +502 -0
  29. package/lib/rules/enforce-exported-function-types.js +4 -4
  30. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
  31. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +262 -0
  32. package/lib/rules/enforce-firestore-doc-ref-generic.js +350 -3
  33. package/lib/rules/enforce-firestore-facade.js +349 -157
  34. package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
  35. package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
  36. package/lib/rules/enforce-firestore-set-merge.js +3 -4
  37. package/lib/rules/enforce-global-constants.d.ts +4 -1
  38. package/lib/rules/enforce-global-constants.js +240 -12
  39. package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
  40. package/lib/rules/enforce-memoize-async.d.ts +2 -1
  41. package/lib/rules/enforce-memoize-async.js +92 -27
  42. package/lib/rules/enforce-memoize-getters.d.ts +3 -0
  43. package/lib/rules/enforce-memoize-getters.js +147 -0
  44. package/lib/rules/enforce-microdiff.js +3 -2
  45. package/lib/rules/enforce-object-literal-as-const.js +4 -4
  46. package/lib/rules/enforce-positive-naming.js +9 -7
  47. package/lib/rules/enforce-props-argument-name.d.ts +2 -7
  48. package/lib/rules/enforce-props-argument-name.js +165 -151
  49. package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
  50. package/lib/rules/enforce-props-naming-consistency.js +145 -0
  51. package/lib/rules/enforce-querykey-ts.d.ts +9 -0
  52. package/lib/rules/enforce-querykey-ts.js +274 -0
  53. package/lib/rules/enforce-safe-stringify.js +1 -1
  54. package/lib/rules/enforce-serializable-params.js +3 -3
  55. package/lib/rules/enforce-singular-type-names.js +10 -6
  56. package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
  57. package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
  58. package/lib/rules/enforce-storage-context.d.ts +9 -0
  59. package/lib/rules/enforce-storage-context.js +646 -0
  60. package/lib/rules/enforce-timestamp-now.js +23 -7
  61. package/lib/rules/enforce-transform-memoization.d.ts +4 -0
  62. package/lib/rules/enforce-transform-memoization.js +416 -0
  63. package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
  64. package/lib/rules/enforce-typescript-markdown-code-blocks.js +100 -0
  65. package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
  66. package/lib/rules/enforce-unique-cursor-headers.js +365 -0
  67. package/lib/rules/enforce-verb-noun-naming.js +90 -14
  68. package/lib/rules/ensure-pointer-events-none.js +28 -2
  69. package/lib/rules/export-if-in-doubt.js +27 -4
  70. package/lib/rules/extract-global-constants.js +92 -53
  71. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
  72. package/lib/rules/fast-deep-equal-over-microdiff.js +351 -83
  73. package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
  74. package/lib/rules/firestore-transaction-reads-before-writes.js +277 -0
  75. package/lib/rules/flatten-push-calls.d.ts +2 -0
  76. package/lib/rules/flatten-push-calls.js +428 -0
  77. package/lib/rules/generic-starts-with-t.js +8 -2
  78. package/lib/rules/global-const-style.js +50 -19
  79. package/lib/rules/jsdoc-above-field.d.ts +11 -0
  80. package/lib/rules/jsdoc-above-field.js +208 -0
  81. package/lib/rules/key-only-outermost-element.js +21 -3
  82. package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
  83. package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
  84. package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
  85. package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
  86. package/lib/rules/memo-nested-react-components.d.ts +8 -0
  87. package/lib/rules/memo-nested-react-components.js +410 -0
  88. package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
  89. package/lib/rules/memoize-root-level-hocs.js +242 -0
  90. package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
  91. package/lib/rules/no-always-true-false-conditions.js +68 -8
  92. package/lib/rules/no-array-length-in-deps.d.ts +12 -0
  93. package/lib/rules/no-array-length-in-deps.js +314 -0
  94. package/lib/rules/no-async-array-filter.js +5 -2
  95. package/lib/rules/no-async-foreach.js +210 -12
  96. package/lib/rules/no-circular-references.js +18 -17
  97. package/lib/rules/no-class-instance-destructuring.js +55 -12
  98. package/lib/rules/no-complex-cloud-params.js +22 -5
  99. package/lib/rules/no-compositing-layer-props.js +6 -4
  100. package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
  101. package/lib/rules/no-console-error.d.ts +9 -0
  102. package/lib/rules/no-console-error.js +525 -0
  103. package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
  104. package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
  105. package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
  106. package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
  107. package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
  108. package/lib/rules/no-entire-object-hook-deps.js +320 -68
  109. package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
  110. package/lib/rules/no-excessive-parent-chain.js +273 -0
  111. package/lib/rules/no-explicit-return-type.d.ts +2 -1
  112. package/lib/rules/no-explicit-return-type.js +215 -26
  113. package/lib/rules/no-filter-without-return.js +5 -1
  114. package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
  115. package/lib/rules/no-firestore-jest-mock.js +126 -8
  116. package/lib/rules/no-firestore-object-arrays.js +398 -28
  117. package/lib/rules/no-handler-suffix.d.ts +12 -0
  118. package/lib/rules/no-handler-suffix.js +305 -0
  119. package/lib/rules/no-hungarian.js +14 -14
  120. package/lib/rules/no-inline-component-prop.d.ts +10 -0
  121. package/lib/rules/no-inline-component-prop.js +456 -0
  122. package/lib/rules/no-jsx-in-hooks.js +6 -1
  123. package/lib/rules/no-jsx-whitespace-literal.js +8 -2
  124. package/lib/rules/no-margin-properties.d.ts +7 -1
  125. package/lib/rules/no-margin-properties.js +22 -9
  126. package/lib/rules/no-memoize-on-static.js +9 -1
  127. package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
  128. package/lib/rules/no-misleading-boolean-prefixes.js +331 -0
  129. package/lib/rules/no-misused-switch-case.js +22 -1
  130. package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
  131. package/lib/rules/no-mixed-firestore-transactions.js +296 -34
  132. package/lib/rules/no-mock-firebase-admin.js +5 -2
  133. package/lib/rules/no-object-values-on-strings.js +10 -2
  134. package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
  135. package/lib/rules/no-overridable-method-calls-in-constructor.js +272 -0
  136. package/lib/rules/no-passthrough-getters.d.ts +4 -0
  137. package/lib/rules/no-passthrough-getters.js +206 -0
  138. package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
  139. package/lib/rules/no-redundant-annotation-assertion.js +402 -0
  140. package/lib/rules/no-redundant-param-types.js +16 -6
  141. package/lib/rules/no-redundant-this-params.d.ts +3 -0
  142. package/lib/rules/no-redundant-this-params.js +459 -0
  143. package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
  144. package/lib/rules/no-redundant-usecallback-wrapper.js +328 -0
  145. package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
  146. package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
  147. package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
  148. package/lib/rules/no-restricted-properties-fix.js +173 -0
  149. package/lib/rules/no-separate-loading-state.d.ts +8 -0
  150. package/lib/rules/no-separate-loading-state.js +133 -0
  151. package/lib/rules/no-stale-state-across-await.d.ts +1 -0
  152. package/lib/rules/no-stale-state-across-await.js +185 -0
  153. package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
  154. package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
  155. package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
  156. package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
  157. package/lib/rules/no-type-assertion-returns.js +49 -49
  158. package/lib/rules/no-undefined-null-passthrough.js +217 -40
  159. package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
  160. package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
  161. package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
  162. package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
  163. package/lib/rules/no-unnecessary-destructuring.js +14 -4
  164. package/lib/rules/no-unnecessary-verb-suffix.js +2 -3
  165. package/lib/rules/no-unpinned-dependencies.js +36 -5
  166. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  167. package/lib/rules/no-unused-props.d.ts +5 -2
  168. package/lib/rules/no-unused-props.js +451 -112
  169. package/lib/rules/no-unused-usestate.js +6 -2
  170. package/lib/rules/no-useless-fragment.js +28 -2
  171. package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
  172. package/lib/rules/no-useless-usememo-primitives.js +393 -0
  173. package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
  174. package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
  175. package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
  176. package/lib/rules/omit-index-html.d.ts +2 -1
  177. package/lib/rules/omit-index-html.js +62 -7
  178. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  179. package/lib/rules/optimize-object-boolean-conditions.js +236 -0
  180. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  181. package/lib/rules/parallelize-async-operations.js +394 -0
  182. package/lib/rules/prefer-batch-operations.d.ts +1 -3
  183. package/lib/rules/prefer-batch-operations.js +228 -35
  184. package/lib/rules/prefer-block-comments-for-declarations.js +22 -7
  185. package/lib/rules/prefer-clone-deep.js +3 -3
  186. package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
  187. package/lib/rules/prefer-destructuring-no-class.js +244 -59
  188. package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
  189. package/lib/rules/prefer-docsetter-setall.js +243 -0
  190. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  191. package/lib/rules/prefer-document-flattening.js +239 -0
  192. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  193. package/lib/rules/prefer-field-paths-in-transforms.js +251 -0
  194. package/lib/rules/prefer-fragment-component.js +4 -4
  195. package/lib/rules/prefer-fragment-shorthand.js +2 -1
  196. package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
  197. package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
  198. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  199. package/lib/rules/prefer-global-router-state-key.js +305 -31
  200. package/lib/rules/prefer-memoized-props.d.ts +3 -0
  201. package/lib/rules/prefer-memoized-props.js +445 -0
  202. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  203. package/lib/rules/prefer-next-dynamic.js +357 -0
  204. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  205. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +435 -0
  206. package/lib/rules/prefer-nullish-coalescing-override.d.ts +7 -0
  207. package/lib/rules/prefer-nullish-coalescing-override.js +188 -0
  208. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  209. package/lib/rules/prefer-params-over-parent-id.js +277 -0
  210. package/lib/rules/prefer-settings-object.js +23 -15
  211. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  212. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  213. package/lib/rules/prefer-type-over-interface.js +7 -2
  214. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  215. package/lib/rules/prefer-url-tostring-over-tojson.js +169 -0
  216. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  217. package/lib/rules/prefer-use-deep-compare-memo.js +431 -0
  218. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +25 -2
  219. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  220. package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
  221. package/lib/rules/prevent-children-clobber.d.ts +2 -0
  222. package/lib/rules/prevent-children-clobber.js +536 -0
  223. package/lib/rules/react-memoize-literals.d.ts +4 -0
  224. package/lib/rules/react-memoize-literals.js +495 -0
  225. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  226. package/lib/rules/react-usememo-should-be-component.js +144 -78
  227. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  228. package/lib/rules/require-hooks-default-params.js +17 -16
  229. package/lib/rules/require-https-error-cause.d.ts +4 -0
  230. package/lib/rules/require-https-error-cause.js +136 -0
  231. package/lib/rules/require-https-error.js +43 -21
  232. package/lib/rules/require-image-optimized.js +2 -2
  233. package/lib/rules/require-memo.js +12 -2
  234. package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
  235. package/lib/rules/require-memoize-jsx-returners.js +485 -0
  236. package/lib/rules/require-usememo-object-literals.js +9 -4
  237. package/lib/rules/semantic-function-prefixes.js +38 -18
  238. package/lib/rules/sync-onwrite-name-func.js +5 -1
  239. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  240. package/lib/rules/test-file-location-enforcement.js +57 -0
  241. package/lib/rules/use-custom-link.js +6 -1
  242. package/lib/rules/use-custom-router.js +6 -1
  243. package/lib/rules/use-latest-callback.d.ts +3 -0
  244. package/lib/rules/use-latest-callback.js +271 -0
  245. package/lib/rules/vertically-group-related-functions.d.ts +16 -0
  246. package/lib/rules/vertically-group-related-functions.js +480 -0
  247. package/lib/utils/ASTHelpers.d.ts +13 -1
  248. package/lib/utils/ASTHelpers.js +34 -0
  249. package/lib/utils/createRule.js +1 -1
  250. package/lib/utils/getMethodName.d.ts +27 -0
  251. package/lib/utils/getMethodName.js +35 -0
  252. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  253. package/lib/utils/ruleTester.d.ts +1 -0
  254. package/lib/utils/ruleTester.js +4 -1
  255. package/lib/utils/tsTypeClassifier.d.ts +30 -0
  256. package/lib/utils/tsTypeClassifier.js +149 -0
  257. package/package.json +23 -14
  258. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
@@ -1,8 +1,90 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.noFirestoreJestMock = void 0;
7
+ const path_1 = __importDefault(require("path"));
4
8
  const utils_1 = require("@typescript-eslint/utils");
5
9
  const createRule_1 = require("../utils/createRule");
10
+ const FIRESTORE_JEST_MOCK = 'firestore-jest-mock';
11
+ const MOCK_FIRESTORE_TARGET = '__test-utils__/mockFirestore';
12
+ const toPosixPath = (filePath) => filePath.replace(/\\/g, '/');
13
+ const ensureRelativeSpecifier = (specifier) => specifier.startsWith('.') ? specifier : `./${specifier}`;
14
+ const isWindowsDrivePath = (filePath) => /^[A-Za-z]:[\\/]/.test(filePath);
15
+ const isValidRelativePath = (relativePath) => !path_1.default.isAbsolute(relativePath) && !isWindowsDrivePath(relativePath);
16
+ const buildReplacementPath = (sourceFilePath, cwd) => {
17
+ const absoluteFilename = path_1.default.isAbsolute(sourceFilePath)
18
+ ? sourceFilePath
19
+ : path_1.default.join(cwd, sourceFilePath);
20
+ const targetPath = path_1.default.join(cwd, MOCK_FIRESTORE_TARGET);
21
+ const relativePath = path_1.default.relative(path_1.default.dirname(absoluteFilename), targetPath);
22
+ if (!isValidRelativePath(relativePath)) {
23
+ return '';
24
+ }
25
+ return ensureRelativeSpecifier(toPosixPath(relativePath));
26
+ };
27
+ const findVariableDeclarator = (node) => {
28
+ const { parent } = node;
29
+ if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
30
+ return parent;
31
+ }
32
+ if (parent?.type === utils_1.AST_NODE_TYPES.AwaitExpression &&
33
+ parent.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
34
+ return parent.parent;
35
+ }
36
+ return null;
37
+ };
38
+ const buildDestructuringFix = (fixer, pattern) => {
39
+ if (pattern.properties.length !== 1) {
40
+ return null;
41
+ }
42
+ const [property] = pattern.properties;
43
+ if (property.type !== utils_1.AST_NODE_TYPES.Property) {
44
+ return null;
45
+ }
46
+ if (property.value.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
47
+ return null;
48
+ }
49
+ if (property.value.type !== utils_1.AST_NODE_TYPES.Identifier) {
50
+ return null;
51
+ }
52
+ const localName = property.value.name;
53
+ const replacement = localName === 'mockFirestore'
54
+ ? '{ mockFirestore }'
55
+ : `{ mockFirestore: ${localName} }`;
56
+ return fixer.replaceText(pattern, replacement);
57
+ };
58
+ const constructImportStatement = (localName, replacementPath) => {
59
+ const binding = localName === 'mockFirestore'
60
+ ? 'mockFirestore'
61
+ : `mockFirestore as ${localName}`;
62
+ return `import { ${binding} } from '${replacementPath}';`;
63
+ };
64
+ const getSingleValueImportSpecifier = (node) => {
65
+ const valueSpecifiers = node.specifiers.filter((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
66
+ specifier.importKind !== 'type');
67
+ const hasTypeSpecifiers = node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
68
+ specifier.importKind === 'type');
69
+ const hasUnsupportedSpecifier = node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ||
70
+ specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier);
71
+ if (valueSpecifiers.length !== 1 ||
72
+ hasTypeSpecifiers ||
73
+ hasUnsupportedSpecifier) {
74
+ return null;
75
+ }
76
+ return valueSpecifiers[0];
77
+ };
78
+ const buildImportDeclarationFix = (node, replacementPath) => {
79
+ if (!replacementPath) {
80
+ return null;
81
+ }
82
+ const specifier = getSingleValueImportSpecifier(node);
83
+ if (!specifier) {
84
+ return null;
85
+ }
86
+ return constructImportStatement(specifier.local.name, replacementPath);
87
+ };
6
88
  exports.noFirestoreJestMock = (0, createRule_1.createRule)({
7
89
  name: 'no-firestore-jest-mock',
8
90
  meta: {
@@ -14,38 +96,72 @@ exports.noFirestoreJestMock = (0, createRule_1.createRule)({
14
96
  fixable: 'code',
15
97
  schema: [],
16
98
  messages: {
17
- noFirestoreJestMock: 'Do not import from firestore-jest-mock. Use mockFirestore from the centralized mock utility instead.',
99
+ noFirestoreJestMock: 'What\'s wrong: This test imports "{{moduleName}}" directly Why it matters: it bypasses the centralized mockFirestore helper that mirrors production schema and keeps seeding/cleanup consistent, which leads to inconsistent data and flaky tests → How to fix: import { mockFirestore } from "{{replacementPath}}" instead.',
18
100
  },
19
101
  },
20
102
  defaultOptions: [],
21
103
  create(context) {
22
- const filename = context.getFilename();
104
+ const sourceFilePath = context.getFilename();
23
105
  // Only apply rule to test files
24
- if (!filename.endsWith('.test.ts')) {
106
+ if (!sourceFilePath.endsWith('.test.ts')) {
25
107
  return {};
26
108
  }
109
+ const cwd = typeof context.getCwd === 'function' ? context.getCwd() : process.cwd();
110
+ const replacementPath = buildReplacementPath(sourceFilePath, cwd);
111
+ const messageReplacementPath = replacementPath ||
112
+ ensureRelativeSpecifier(toPosixPath(MOCK_FIRESTORE_TARGET));
113
+ const reportData = {
114
+ moduleName: FIRESTORE_JEST_MOCK,
115
+ replacementPath: messageReplacementPath,
116
+ };
27
117
  return {
28
118
  ImportDeclaration(node) {
29
119
  // Skip type imports completely
30
120
  if (node.importKind === 'type') {
31
121
  return;
32
122
  }
33
- if (node.source.value === 'firestore-jest-mock') {
123
+ if (node.source.value === FIRESTORE_JEST_MOCK) {
34
124
  context.report({
35
125
  node,
36
126
  messageId: 'noFirestoreJestMock',
127
+ data: reportData,
37
128
  fix: (fixer) => {
38
- return fixer.replaceText(node, `import { mockFirestore } from '../../../../../__test-utils__/mockFirestore';`);
129
+ const replacementImport = buildImportDeclarationFix(node, replacementPath);
130
+ if (!replacementImport) {
131
+ return null;
132
+ }
133
+ return fixer.replaceText(node, replacementImport);
39
134
  },
40
135
  });
41
136
  }
42
137
  },
43
138
  ImportExpression(node) {
44
139
  if (node.source.type === utils_1.AST_NODE_TYPES.Literal &&
45
- node.source.value === 'firestore-jest-mock') {
140
+ node.source.value === FIRESTORE_JEST_MOCK) {
141
+ const variableDeclarator = findVariableDeclarator(node);
46
142
  context.report({
47
143
  node,
48
144
  messageId: 'noFirestoreJestMock',
145
+ data: reportData,
146
+ fix: (fixer) => {
147
+ if (!replacementPath) {
148
+ return null;
149
+ }
150
+ if (!variableDeclarator) {
151
+ return null;
152
+ }
153
+ if (variableDeclarator.id.type !== utils_1.AST_NODE_TYPES.ObjectPattern) {
154
+ return null;
155
+ }
156
+ const destructuringFix = buildDestructuringFix(fixer, variableDeclarator.id);
157
+ if (!destructuringFix) {
158
+ return null;
159
+ }
160
+ return [
161
+ fixer.replaceText(node.source, `'${replacementPath}'`),
162
+ destructuringFix,
163
+ ];
164
+ },
49
165
  });
50
166
  }
51
167
  },
@@ -58,10 +174,11 @@ exports.noFirestoreJestMock = (0, createRule_1.createRule)({
58
174
  node.callee.property.name === 'mock' &&
59
175
  node.arguments.length > 0 &&
60
176
  node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal &&
61
- node.arguments[0].value === 'firestore-jest-mock') {
177
+ node.arguments[0].value === FIRESTORE_JEST_MOCK) {
62
178
  context.report({
63
179
  node,
64
180
  messageId: 'noFirestoreJestMock',
181
+ data: reportData,
65
182
  });
66
183
  }
67
184
  // Check for require('firestore-jest-mock')
@@ -69,10 +186,11 @@ exports.noFirestoreJestMock = (0, createRule_1.createRule)({
69
186
  node.callee.name === 'require' &&
70
187
  node.arguments.length > 0 &&
71
188
  node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal &&
72
- node.arguments[0].value === 'firestore-jest-mock') {
189
+ node.arguments[0].value === FIRESTORE_JEST_MOCK) {
73
190
  context.report({
74
191
  node,
75
192
  messageId: 'noFirestoreJestMock',
193
+ data: reportData,
76
194
  });
77
195
  }
78
196
  },
@@ -13,44 +13,162 @@ const PRIMITIVE_TYPES = new Set([
13
13
  'undefined',
14
14
  'GeoPoint',
15
15
  ]);
16
+ const UNKNOWN_FIELD_LABEL = '<unknown field>';
16
17
  const isInFirestoreTypesDirectory = (filename) => {
17
- return filename.includes('functions/src/types/firestore');
18
+ if (!filename || filename === '<input>')
19
+ return false;
20
+ const normalized = filename.replace(/\\/g, '/');
21
+ if (normalized.includes('/node_modules/') || normalized.includes('/dist/')) {
22
+ return false;
23
+ }
24
+ // Match typical mono/multi-repo layouts
25
+ return normalized.includes('/types/firestore/');
18
26
  };
19
- const isObjectType = (node) => {
20
- switch (node.type) {
21
- case utils_1.AST_NODE_TYPES.TSTypeLiteral:
22
- return true;
23
- case utils_1.AST_NODE_TYPES.TSTypeReference:
24
- if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
25
- const typeName = node.typeName.name;
26
- return !PRIMITIVE_TYPES.has(typeName);
27
+ const getRightmostIdentifierName = (name) => {
28
+ let cur = name;
29
+ // Walk rightwards until we reach an Identifier
30
+ while (cur.type !== utils_1.AST_NODE_TYPES.Identifier) {
31
+ cur = cur.right;
32
+ }
33
+ return cur.name;
34
+ };
35
+ const isArrayGenericReference = (node) => {
36
+ return (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
37
+ (node.typeName.name === 'Array' || node.typeName.name === 'ReadonlyArray'));
38
+ };
39
+ const isParenthesizedType = (node) => {
40
+ if (!node || typeof node !== 'object')
41
+ return false;
42
+ const candidate = node;
43
+ return (candidate.type === 'TSParenthesizedType' &&
44
+ candidate.typeAnnotation !== undefined);
45
+ };
46
+ const unwrapArrayElementType = (node) => {
47
+ let current = node;
48
+ // Fixpoint loop: peel wrappers in any order until none remain
49
+ // Cap unwrap iterations to prevent accidental non-terminating edits if new cases are added.
50
+ // Wrappers are finite; 10 is generous but safe.
51
+ for (let i = 0; i < 10; i++) {
52
+ if (current.type === utils_1.AST_NODE_TYPES.TSTypeOperator &&
53
+ current.operator === 'readonly') {
54
+ current = current
55
+ .typeAnnotation;
56
+ continue;
57
+ }
58
+ if (isParenthesizedType(current)) {
59
+ const paren = current;
60
+ current = paren.typeAnnotation;
61
+ continue;
62
+ }
63
+ if (current.type === utils_1.AST_NODE_TYPES.TSArrayType) {
64
+ current = current.elementType;
65
+ continue;
66
+ }
67
+ if (current.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
68
+ isArrayGenericReference(current) &&
69
+ current.typeParameters &&
70
+ current.typeParameters.params.length > 0) {
71
+ current = current.typeParameters.params[0];
72
+ continue;
73
+ }
74
+ break;
75
+ }
76
+ return current;
77
+ };
78
+ const literalToString = (node) => {
79
+ if (!node || node.type !== utils_1.AST_NODE_TYPES.Literal)
80
+ return null;
81
+ const value = node.value;
82
+ if (typeof value === 'string' ||
83
+ typeof value === 'number' ||
84
+ typeof value === 'bigint') {
85
+ return String(value);
86
+ }
87
+ return null;
88
+ };
89
+ /**
90
+ * Derives a field label for diagnostics by walking ancestors.
91
+ * Prefers TSPropertySignature keys and returns identifier or string/number/bigint literal keys when present.
92
+ * Falls back to the surrounding TSTypeAliasDeclaration or TSInterfaceDeclaration name when no property key is available.
93
+ * This ensures declaration-level array aliases (for example, `type Foo = Object[]`) surface a meaningful label and avoid unlabeled diagnostics.
94
+ * Returns a placeholder when resolution fails so downstream messaging remains readable.
95
+ */
96
+ const getFieldName = (node) => {
97
+ let current = node;
98
+ while (current) {
99
+ if (current.type === utils_1.AST_NODE_TYPES.TSPropertySignature) {
100
+ const key = current.key;
101
+ if (key.type === utils_1.AST_NODE_TYPES.Identifier)
102
+ return key.name;
103
+ const literalKey = literalToString(key);
104
+ if (literalKey !== null)
105
+ return literalKey;
106
+ const computedKey = key;
107
+ if (computedKey.type === 'TSComputedPropertyName' &&
108
+ computedKey.expression) {
109
+ const expr = computedKey.expression;
110
+ if (expr.type === utils_1.AST_NODE_TYPES.Identifier)
111
+ return expr.name;
112
+ const literalExpr = literalToString(expr);
113
+ if (literalExpr !== null)
114
+ return literalExpr;
27
115
  }
28
- else if (node.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
29
- // Handle namespace.Type cases
30
- return true;
116
+ }
117
+ if (current.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration ||
118
+ current.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
119
+ const id = current.id;
120
+ if (id.type === utils_1.AST_NODE_TYPES.Identifier) {
121
+ return id.name;
31
122
  }
32
- return true;
33
- case utils_1.AST_NODE_TYPES.TSIntersectionType:
34
- case utils_1.AST_NODE_TYPES.TSUnionType:
35
- return node.types.some(isObjectType);
36
- case utils_1.AST_NODE_TYPES.TSMappedType:
37
- case utils_1.AST_NODE_TYPES.TSIndexedAccessType:
38
- return true;
39
- default:
123
+ }
124
+ current = current.parent;
125
+ }
126
+ return UNKNOWN_FIELD_LABEL;
127
+ };
128
+ // Determine whether this type node appears in a Firestore model type context
129
+ // i.e., within an interface or type alias declaration, and not within variable/function annotations
130
+ const isInModelTypeContext = (node) => {
131
+ let current = node.parent;
132
+ while (current) {
133
+ // Hard stop for non-type declaration contexts
134
+ if (current.type === utils_1.AST_NODE_TYPES.VariableDeclarator ||
135
+ current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
136
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
137
+ current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
138
+ current.type === utils_1.AST_NODE_TYPES.MethodDefinition ||
139
+ current.type === utils_1.AST_NODE_TYPES.TSMethodSignature ||
140
+ current.type === utils_1.AST_NODE_TYPES.ClassDeclaration ||
141
+ current.type === utils_1.AST_NODE_TYPES.PropertyDefinition ||
142
+ current.type === utils_1.AST_NODE_TYPES.TSParameterProperty) {
40
143
  return false;
144
+ }
145
+ if (current.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration ||
146
+ current.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
147
+ return true;
148
+ }
149
+ current = current.parent;
41
150
  }
151
+ return false;
42
152
  };
43
153
  exports.noFirestoreObjectArrays = (0, createRule_1.createRule)({
44
154
  name: 'no-firestore-object-arrays',
45
155
  meta: {
46
156
  type: 'problem',
47
157
  docs: {
48
- description: 'Disallow arrays of objects in Firestore type definitions to optimize performance and avoid unnecessary fetches',
158
+ description: 'Disallow arrays of object types in Firestore models. Prefer Record maps keyed by id with an index field, or subcollections/arrays of IDs.',
49
159
  recommended: 'warn',
160
+ requiresTypeChecking: false,
50
161
  },
51
162
  schema: [],
52
163
  messages: {
53
- noObjectArrays: 'Arrays of objects are not recommended in Firestore. Use subcollections, arrays of IDs, or structured maps (Record<string, T>) instead.',
164
+ noObjectArrays: [
165
+ // What's wrong
166
+ "What's wrong: {{fieldName}} stores an array of objects in a Firestore document.",
167
+ // Why it matters
168
+ 'Why it matters: Firestore cannot query inside object arrays, and updating one item rewrites the whole array; concurrent writes can overwrite each other and lose data.',
169
+ // How to fix
170
+ 'How to fix: Store items as Record<string, T> keyed by id with an index field for ordering (use toMap/toArr helpers), or move items into a subcollection or store only an array of IDs.',
171
+ ].join('\n'),
54
172
  },
55
173
  },
56
174
  defaultOptions: [],
@@ -58,25 +176,277 @@ exports.noFirestoreObjectArrays = (0, createRule_1.createRule)({
58
176
  if (!isInFirestoreTypesDirectory(context.getFilename())) {
59
177
  return {};
60
178
  }
179
+ const sourceCode = context.sourceCode;
180
+ // Collect alias/interface/enum information within this file to refine object vs primitive classification
181
+ const aliasNameToType = new Map();
182
+ const interfaceNames = new Set();
183
+ const enumNames = new Set();
184
+ const visitNode = (n) => {
185
+ switch (n.type) {
186
+ case utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration: {
187
+ aliasNameToType.set(n.id.name, n.typeAnnotation);
188
+ break;
189
+ }
190
+ case utils_1.AST_NODE_TYPES.TSInterfaceDeclaration: {
191
+ interfaceNames.add(n.id.name);
192
+ break;
193
+ }
194
+ case utils_1.AST_NODE_TYPES.TSEnumDeclaration: {
195
+ enumNames.add(n.id.name);
196
+ break;
197
+ }
198
+ case utils_1.AST_NODE_TYPES.ExportNamedDeclaration: {
199
+ if (n.declaration)
200
+ visitNode(n.declaration);
201
+ break;
202
+ }
203
+ case utils_1.AST_NODE_TYPES.ExportDefaultDeclaration: {
204
+ if (n.declaration) {
205
+ visitNode(n.declaration);
206
+ }
207
+ break;
208
+ }
209
+ case utils_1.AST_NODE_TYPES.TSExportAssignment: {
210
+ if (n.expression) {
211
+ visitNode(n.expression);
212
+ }
213
+ break;
214
+ }
215
+ case utils_1.AST_NODE_TYPES.TSModuleDeclaration: {
216
+ const body = n.body || null;
217
+ if (body && body.type === utils_1.AST_NODE_TYPES.TSModuleBlock) {
218
+ for (const stmt of body.body) {
219
+ visitNode(stmt);
220
+ }
221
+ }
222
+ else if (body && body.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration) {
223
+ visitNode(body);
224
+ }
225
+ break;
226
+ }
227
+ default: {
228
+ // Only traverse a shallow subset we care about
229
+ break;
230
+ }
231
+ }
232
+ };
233
+ for (const stmt of sourceCode.ast.body) {
234
+ visitNode(stmt);
235
+ }
236
+ const seenAlias = new Set();
237
+ const visitingAliases = new Set();
238
+ const isPrimitiveLikeAlias = (name, recursionDepth) => {
239
+ if (seenAlias.has(name))
240
+ return true;
241
+ if (recursionDepth > 50)
242
+ return false;
243
+ if (PRIMITIVE_TYPES.has(name))
244
+ return true;
245
+ if (enumNames.has(name))
246
+ return true; // enums are non-object primitives for our purposes
247
+ const aliased = aliasNameToType.get(name);
248
+ if (!aliased)
249
+ return false;
250
+ if (visitingAliases.has(name)) {
251
+ return false;
252
+ }
253
+ visitingAliases.add(name);
254
+ seenAlias.add(name);
255
+ // Determine if the alias ultimately resolves to only primitive-like/literal types
256
+ const node = aliased;
257
+ const result = isPrimitiveLikeTypeNode(node, recursionDepth + 1);
258
+ visitingAliases.delete(name);
259
+ if (result) {
260
+ seenAlias.add(name);
261
+ }
262
+ else {
263
+ // Remove optimistic marking when resolution fails
264
+ seenAlias.delete(name);
265
+ }
266
+ return result;
267
+ };
268
+ const isPrimitiveLikeTypeNode = (node, recursionDepth = 0) => {
269
+ if (recursionDepth > 25) {
270
+ return false;
271
+ }
272
+ if (isParenthesizedType(node)) {
273
+ const paren = node;
274
+ return isPrimitiveLikeTypeNode(paren.typeAnnotation, recursionDepth + 1);
275
+ }
276
+ switch (node.type) {
277
+ case utils_1.AST_NODE_TYPES.TSStringKeyword:
278
+ case utils_1.AST_NODE_TYPES.TSNumberKeyword:
279
+ case utils_1.AST_NODE_TYPES.TSBooleanKeyword:
280
+ case utils_1.AST_NODE_TYPES.TSNullKeyword:
281
+ case utils_1.AST_NODE_TYPES.TSUndefinedKeyword:
282
+ case utils_1.AST_NODE_TYPES.TSNeverKeyword:
283
+ return true;
284
+ case utils_1.AST_NODE_TYPES.TSAnyKeyword:
285
+ case utils_1.AST_NODE_TYPES.TSUnknownKeyword:
286
+ return false;
287
+ case utils_1.AST_NODE_TYPES.TSLiteralType:
288
+ return true; // string/number/boolean literals
289
+ case utils_1.AST_NODE_TYPES.TSTypeReference: {
290
+ // Allow known primitive-like references and enums or primitive-like aliases
291
+ const ref = node;
292
+ if (ref.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
293
+ const name = ref.typeName.name;
294
+ if (PRIMITIVE_TYPES.has(name) || enumNames.has(name))
295
+ return true;
296
+ if (seenAlias.has(name))
297
+ return true;
298
+ return isPrimitiveLikeAlias(name, recursionDepth + 1);
299
+ }
300
+ if (ref.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
301
+ const name = getRightmostIdentifierName(ref.typeName);
302
+ if (PRIMITIVE_TYPES.has(name) || enumNames.has(name))
303
+ return true;
304
+ if (seenAlias.has(name))
305
+ return true;
306
+ return isPrimitiveLikeAlias(name, recursionDepth + 1);
307
+ }
308
+ return false;
309
+ }
310
+ case utils_1.AST_NODE_TYPES.TSUnionType: {
311
+ return node.types.every((t) => isPrimitiveLikeTypeNode(t, recursionDepth + 1));
312
+ }
313
+ case utils_1.AST_NODE_TYPES.TSTypeOperator: {
314
+ // Treat keyof/unique symbol/etc as primitive-like to avoid false positives
315
+ if (node.operator !== 'readonly')
316
+ return true;
317
+ return isPrimitiveLikeTypeNode(node
318
+ .typeAnnotation, recursionDepth + 1);
319
+ }
320
+ case utils_1.AST_NODE_TYPES.TSTemplateLiteralType: {
321
+ // Template literal types behave like strings
322
+ return true;
323
+ }
324
+ default:
325
+ return false;
326
+ }
327
+ };
328
+ const isObjectType = (node) => {
329
+ if (isParenthesizedType(node)) {
330
+ const paren = node;
331
+ return isObjectType(paren.typeAnnotation);
332
+ }
333
+ switch (node.type) {
334
+ case utils_1.AST_NODE_TYPES.TSTypeLiteral:
335
+ return true;
336
+ case utils_1.AST_NODE_TYPES.TSTypeReference: {
337
+ const tn = node.typeName;
338
+ if (tn.type !== utils_1.AST_NODE_TYPES.Identifier &&
339
+ tn.type !== utils_1.AST_NODE_TYPES.TSQualifiedName) {
340
+ // Unsupported reference (e.g., ThisType) — do not assume object to avoid false positives
341
+ return false;
342
+ }
343
+ const refName = tn.type === utils_1.AST_NODE_TYPES.Identifier
344
+ ? tn.name
345
+ : getRightmostIdentifierName(tn);
346
+ if (PRIMITIVE_TYPES.has(refName))
347
+ return false;
348
+ if (interfaceNames.has(refName))
349
+ return true;
350
+ if (enumNames.has(refName))
351
+ return false;
352
+ if (aliasNameToType.has(refName)) {
353
+ return !isPrimitiveLikeAlias(refName, 0);
354
+ }
355
+ // Unknown reference: do not assume object to avoid false positives
356
+ return false;
357
+ }
358
+ case utils_1.AST_NODE_TYPES.TSIntersectionType:
359
+ case utils_1.AST_NODE_TYPES.TSUnionType:
360
+ return node.types.some(isObjectType);
361
+ case utils_1.AST_NODE_TYPES.TSMappedType:
362
+ return true;
363
+ case utils_1.AST_NODE_TYPES.TSIndexedAccessType:
364
+ // Treat indexed access as object-like to align with existing tests
365
+ return true;
366
+ case utils_1.AST_NODE_TYPES.TSTypeOperator:
367
+ if (node.operator === 'readonly') {
368
+ return isObjectType(node
369
+ .typeAnnotation);
370
+ }
371
+ return false;
372
+ case utils_1.AST_NODE_TYPES.TSAnyKeyword:
373
+ case utils_1.AST_NODE_TYPES.TSUnknownKeyword:
374
+ return true;
375
+ default:
376
+ return false;
377
+ }
378
+ };
379
+ const skipReadonlyAndParens = (parent) => {
380
+ let currentParent = parent;
381
+ while (currentParent) {
382
+ if (currentParent.type === utils_1.AST_NODE_TYPES.TSTypeOperator &&
383
+ currentParent.operator === 'readonly') {
384
+ currentParent = currentParent.parent;
385
+ continue;
386
+ }
387
+ if (isParenthesizedType(currentParent)) {
388
+ const paren = currentParent;
389
+ currentParent = paren.parent;
390
+ continue;
391
+ }
392
+ break;
393
+ }
394
+ return currentParent;
395
+ };
396
+ const isImmediatelyWrappedByArraySyntax = (node) => {
397
+ const parent = skipReadonlyAndParens(node.parent);
398
+ if (!parent)
399
+ return false;
400
+ if (parent.type === utils_1.AST_NODE_TYPES.TSArrayType)
401
+ return true;
402
+ if (parent.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
403
+ isArrayGenericReference(parent))
404
+ return true;
405
+ return false;
406
+ };
61
407
  return {
62
408
  TSArrayType(node) {
63
- if (isObjectType(node.elementType)) {
409
+ // Only consider arrays within model type/interface declarations
410
+ if (!isInModelTypeContext(node)) {
411
+ return;
412
+ }
413
+ if (isImmediatelyWrappedByArraySyntax(node)) {
414
+ return;
415
+ }
416
+ const base = unwrapArrayElementType(node.elementType);
417
+ if (isObjectType(base)) {
64
418
  context.report({
65
419
  node,
66
420
  messageId: 'noObjectArrays',
421
+ data: {
422
+ fieldName: getFieldName(node),
423
+ },
67
424
  });
68
425
  }
69
426
  },
70
427
  TSTypeReference(node) {
71
- // Handle Array<T> and ReadonlyArray<T> syntax
72
- const typeName = node.typeName.name;
73
- if ((typeName === 'Array' || typeName === 'ReadonlyArray') &&
428
+ if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
429
+ (node.typeName.name === 'Array' ||
430
+ node.typeName.name === 'ReadonlyArray') &&
74
431
  node.typeParameters) {
75
- const elementType = node.typeParameters.params[0];
76
- if (isObjectType(elementType)) {
432
+ // Only consider arrays within model type/interface declarations
433
+ if (!isInModelTypeContext(node)) {
434
+ return;
435
+ }
436
+ if (isImmediatelyWrappedByArraySyntax(node)) {
437
+ return;
438
+ }
439
+ const elementType = node.typeParameters.params[0] ?? null;
440
+ if (!elementType)
441
+ return;
442
+ const base = unwrapArrayElementType(elementType);
443
+ if (isObjectType(base)) {
77
444
  context.report({
78
445
  node,
79
446
  messageId: 'noObjectArrays',
447
+ data: {
448
+ fieldName: getFieldName(node),
449
+ },
80
450
  });
81
451
  }
82
452
  }
@@ -0,0 +1,12 @@
1
+ type Options = [
2
+ {
3
+ ignoreClassMethods?: boolean;
4
+ ignoreInterfaceImplementations?: boolean;
5
+ interfaceAllowlist?: string[];
6
+ allowNames?: string[];
7
+ allowPatterns?: string[];
8
+ allowFilePatterns?: string[];
9
+ }
10
+ ];
11
+ export declare const noHandlerSuffix: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"handlerSuffix", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
12
+ export {};