@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
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Enforce the use of event.params over .ref.parent.id in Firebase change handlers
4
+ * @author BluMint
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.preferParamsOverParentId = void 0;
8
+ const utils_1 = require("@typescript-eslint/utils");
9
+ const createRule_1 = require("../utils/createRule");
10
+ const HANDLER_TYPES = new Set([
11
+ 'DocumentChangeHandler',
12
+ 'DocumentChangeHandlerTransaction',
13
+ 'RealtimeDbChangeHandler',
14
+ 'RealtimeDbChangeHandlerTransaction',
15
+ ]);
16
+ const getQualifiedNameIdentifier = (typeName) => {
17
+ if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
18
+ return typeName.name;
19
+ }
20
+ if (typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName &&
21
+ typeName.right.type === utils_1.AST_NODE_TYPES.Identifier) {
22
+ return typeName.right.name;
23
+ }
24
+ return null;
25
+ };
26
+ const checkTypeAnnotationForHandler = (typeNode) => {
27
+ switch (typeNode.type) {
28
+ case utils_1.AST_NODE_TYPES.TSTypeReference: {
29
+ const typeIdentifier = getQualifiedNameIdentifier(typeNode.typeName);
30
+ return typeIdentifier ? HANDLER_TYPES.has(typeIdentifier) : false;
31
+ }
32
+ case utils_1.AST_NODE_TYPES.TSUnionType:
33
+ return typeNode.types.some(checkTypeAnnotationForHandler);
34
+ case utils_1.AST_NODE_TYPES.TSIntersectionType:
35
+ return typeNode.types.some(checkTypeAnnotationForHandler);
36
+ default:
37
+ return false;
38
+ }
39
+ };
40
+ const findTypeAnnotationInContext = (node) => {
41
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
42
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
43
+ node.parent.id.typeAnnotation) {
44
+ return node.parent.id.typeAnnotation;
45
+ }
46
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
47
+ node.parent.left.type === utils_1.AST_NODE_TYPES.Identifier &&
48
+ node.parent.left.typeAnnotation) {
49
+ return node.parent.left.typeAnnotation;
50
+ }
51
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.Property &&
52
+ node.parent.value === node) {
53
+ let current = node.parent.parent;
54
+ while (current) {
55
+ if (current.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
56
+ current.id.type === utils_1.AST_NODE_TYPES.Identifier &&
57
+ current.id.typeAnnotation) {
58
+ return current.id.typeAnnotation;
59
+ }
60
+ current = current.parent;
61
+ }
62
+ }
63
+ return undefined;
64
+ };
65
+ const isFirebaseChangeHandler = (node) => {
66
+ if (node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
67
+ node.type !== utils_1.AST_NODE_TYPES.FunctionExpression &&
68
+ node.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration) {
69
+ return false;
70
+ }
71
+ const typeAnnotation = findTypeAnnotationInContext(node);
72
+ if (!typeAnnotation) {
73
+ return false;
74
+ }
75
+ return checkTypeAnnotationForHandler(typeAnnotation.typeAnnotation);
76
+ };
77
+ const isParentIdAccess = (node) => {
78
+ if (node.property.type !== utils_1.AST_NODE_TYPES.Identifier ||
79
+ node.property.name !== 'id') {
80
+ return { isMatch: false, depth: 0 };
81
+ }
82
+ const chain = [];
83
+ let current = node.object;
84
+ while (current && current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
85
+ if (current.property.type !== utils_1.AST_NODE_TYPES.Identifier) {
86
+ return { isMatch: false, depth: 0 };
87
+ }
88
+ chain.unshift(current.property.name);
89
+ current = current.object;
90
+ }
91
+ if (chain.length < 2) {
92
+ return { isMatch: false, depth: 0 };
93
+ }
94
+ const refIndex = chain.lastIndexOf('ref');
95
+ if (refIndex === -1) {
96
+ return { isMatch: false, depth: 0 };
97
+ }
98
+ const parentSegment = chain.slice(refIndex + 1);
99
+ if (parentSegment.length === 0) {
100
+ return { isMatch: false, depth: 0 };
101
+ }
102
+ const invalidParent = parentSegment.some((segment) => segment !== 'parent');
103
+ if (invalidParent) {
104
+ return { isMatch: false, depth: 0 };
105
+ }
106
+ const depth = parentSegment.length;
107
+ return { isMatch: depth > 0, depth };
108
+ };
109
+ const getParentParamName = (depth) => {
110
+ if (depth === 1) {
111
+ return 'userId';
112
+ }
113
+ if (depth === 2) {
114
+ return 'parentId';
115
+ }
116
+ return `parent${depth}Id`;
117
+ };
118
+ const findHandlerFunction = (node, handlerNodes) => {
119
+ let current = node;
120
+ while (current) {
121
+ if (handlerNodes.has(current)) {
122
+ return current;
123
+ }
124
+ current = current.parent;
125
+ }
126
+ return null;
127
+ };
128
+ const hasOptionalChaining = (node) => {
129
+ let current = node;
130
+ while (current && current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
131
+ if (current.optional) {
132
+ return true;
133
+ }
134
+ current = current.object;
135
+ }
136
+ return false;
137
+ };
138
+ const findParamsInPattern = (pattern) => {
139
+ for (const prop of pattern.properties) {
140
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
141
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
142
+ prop.key.name === 'params') {
143
+ if (prop.value.type === utils_1.AST_NODE_TYPES.Identifier) {
144
+ return { identifier: prop.value.name };
145
+ }
146
+ if (prop.value.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
147
+ const properties = new Map();
148
+ for (const p of prop.value.properties) {
149
+ if (p.type === utils_1.AST_NODE_TYPES.Property &&
150
+ p.key.type === utils_1.AST_NODE_TYPES.Identifier &&
151
+ p.value.type === utils_1.AST_NODE_TYPES.Identifier) {
152
+ properties.set(p.key.name, p.value.name);
153
+ }
154
+ }
155
+ return { properties };
156
+ }
157
+ }
158
+ }
159
+ return null;
160
+ };
161
+ const getParamsInScope = (handlerNode) => {
162
+ if (handlerNode.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
163
+ handlerNode.type !== utils_1.AST_NODE_TYPES.FunctionExpression &&
164
+ handlerNode.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration) {
165
+ return null;
166
+ }
167
+ const firstParam = handlerNode.params[0];
168
+ if (!firstParam) {
169
+ return null;
170
+ }
171
+ if (firstParam.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
172
+ const paramsInScope = findParamsInPattern(firstParam);
173
+ if (paramsInScope) {
174
+ return paramsInScope;
175
+ }
176
+ }
177
+ if (handlerNode.body &&
178
+ handlerNode.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
179
+ const eventParamName = firstParam.type === utils_1.AST_NODE_TYPES.Identifier ? firstParam.name : 'event';
180
+ for (const statement of handlerNode.body.body) {
181
+ if (statement.type !== utils_1.AST_NODE_TYPES.VariableDeclaration) {
182
+ continue;
183
+ }
184
+ for (const declarator of statement.declarations) {
185
+ if (declarator.id.type !== utils_1.AST_NODE_TYPES.ObjectPattern ||
186
+ !declarator.init ||
187
+ declarator.init.type !== utils_1.AST_NODE_TYPES.Identifier ||
188
+ declarator.init.name !== eventParamName) {
189
+ continue;
190
+ }
191
+ const paramsInScope = findParamsInPattern(declarator.id);
192
+ if (paramsInScope) {
193
+ return paramsInScope;
194
+ }
195
+ }
196
+ }
197
+ }
198
+ return null;
199
+ };
200
+ exports.preferParamsOverParentId = (0, createRule_1.createRule)({
201
+ name: 'prefer-params-over-parent-id',
202
+ meta: {
203
+ type: 'suggestion',
204
+ docs: {
205
+ description: 'Prefer handler params for parent IDs instead of traversing ref.parent.id so Firebase triggers stay aligned with path templates and type-safe.',
206
+ recommended: 'error',
207
+ },
208
+ fixable: 'code',
209
+ schema: [],
210
+ messages: {
211
+ preferParams: [
212
+ "What's wrong: This code reads an ID via `ref.parent...id` instead of using the trigger's params.",
213
+ '',
214
+ 'Why it matters: Walking `ref.parent` ties the handler to the current path depth; when collections change, it can yield the wrong ID (or a collection name) and bypasses the typed params the trigger provides.',
215
+ '',
216
+ 'How to fix: Read the ID from `params.{{paramName}}` (or destructure `const { params } = event` and then access `params.{{paramName}}`).',
217
+ ].join('\n'),
218
+ },
219
+ },
220
+ defaultOptions: [],
221
+ create(context) {
222
+ const handlerNodes = new Set();
223
+ return {
224
+ // Track Firebase change handler functions
225
+ 'FunctionDeclaration, FunctionExpression, ArrowFunctionExpression'(node) {
226
+ if (isFirebaseChangeHandler(node)) {
227
+ handlerNodes.add(node);
228
+ }
229
+ },
230
+ // Detect .ref.parent.id patterns
231
+ MemberExpression(node) {
232
+ const parentAccess = isParentIdAccess(node);
233
+ if (parentAccess.isMatch) {
234
+ const handlerNode = findHandlerFunction(node, handlerNodes);
235
+ if (handlerNode) {
236
+ const hasOptional = hasOptionalChaining(node);
237
+ const paramsInScope = getParamsInScope(handlerNode);
238
+ if (!paramsInScope) {
239
+ context.report({
240
+ node,
241
+ messageId: 'preferParams',
242
+ data: {
243
+ paramName: getParentParamName(parentAccess.depth),
244
+ },
245
+ });
246
+ return;
247
+ }
248
+ const paramSuggestion = getParentParamName(parentAccess.depth);
249
+ let replacement = null;
250
+ if (paramsInScope.identifier) {
251
+ replacement = hasOptional
252
+ ? `${paramsInScope.identifier}?.${paramSuggestion}`
253
+ : `${paramsInScope.identifier}.${paramSuggestion}`;
254
+ }
255
+ else if (paramsInScope.properties) {
256
+ const localName = paramsInScope.properties.get(paramSuggestion);
257
+ if (localName) {
258
+ replacement = localName;
259
+ }
260
+ }
261
+ context.report({
262
+ node,
263
+ messageId: 'preferParams',
264
+ data: {
265
+ paramName: paramSuggestion,
266
+ },
267
+ fix: replacement
268
+ ? (fixer) => fixer.replaceText(node, replacement)
269
+ : undefined,
270
+ });
271
+ }
272
+ }
273
+ },
274
+ };
275
+ },
276
+ });
277
+ //# sourceMappingURL=prefer-params-over-parent-id.js.map
@@ -40,8 +40,8 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
40
40
  },
41
41
  ],
42
42
  messages: {
43
- tooManyParams: 'Function has too many parameters ({{count}}). Use a settings object instead.',
44
- sameTypeParams: 'Function has multiple parameters of the same type. Use a settings object instead.',
43
+ tooManyParams: "Function accepts {{count}} positional parameters (limit {{minimum}}). Long positional lists hide each argument's meaning and make call sites easy to mis-order. Pass a single settings object so callers name each field and keep the call readable.",
44
+ sameTypeParams: 'Function receives {{paramCount}} positional parameters including multiple "{{type}}" values. Repeated types in positional arguments invite swapped values and force callers to remember parameter order. Replace the positional list with a settings object so each value is labeled and self-documenting.',
45
45
  },
46
46
  },
47
47
  defaultOptions: [defaultOptions],
@@ -60,10 +60,12 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
60
60
  ? typeNode.typeName.name
61
61
  : 'unknown';
62
62
  // If there are type parameters, include them in the type signature
63
- if (typeNode.typeParameters && typeNode.typeParameters.params.length > 0) {
63
+ if (typeNode.typeParameters &&
64
+ typeNode.typeParameters.params.length > 0) {
65
+ const sourceCode = context.sourceCode;
64
66
  const typeParams = typeNode.typeParameters.params
65
- .map(param => param.type)
66
- .join('_');
67
+ .map((param) => sourceCode.getText(param))
68
+ .join(', ');
67
69
  return `${typeName}<${typeParams}>`;
68
70
  }
69
71
  return typeName;
@@ -78,10 +80,12 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
78
80
  ? typeNode.typeName.name
79
81
  : 'unknown';
80
82
  // If there are type parameters, include them in the type signature
81
- if (typeNode.typeParameters && typeNode.typeParameters.params.length > 0) {
83
+ if (typeNode.typeParameters &&
84
+ typeNode.typeParameters.params.length > 0) {
85
+ const sourceCode = context.sourceCode;
82
86
  const typeParams = typeNode.typeParameters.params
83
- .map(param => param.type)
84
- .join('_');
87
+ .map((param) => sourceCode.getText(param))
88
+ .join(', ');
85
89
  return `${typeName}<${typeParams}>`;
86
90
  }
87
91
  return typeName;
@@ -96,17 +100,17 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
96
100
  }
97
101
  return 'unknown';
98
102
  }
99
- function hasSameTypeParameters(params) {
103
+ function findDuplicateParameterType(params) {
100
104
  const typeMap = new Map();
101
105
  for (const param of params) {
102
106
  const type = getParameterType(param);
103
107
  const count = (typeMap.get(type) || 0) + 1;
104
108
  typeMap.set(type, count);
105
109
  if (count > 1) {
106
- return true;
110
+ return type;
107
111
  }
108
112
  }
109
- return false;
113
+ return null;
110
114
  }
111
115
  function isBuiltInOrThirdParty(node) {
112
116
  // Check if the node is part of a new expression (constructor call)
@@ -287,8 +291,10 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
287
291
  // Check if the function name or its type annotation suggests it's a handler with transaction
288
292
  if (functionName.includes('Transaction') ||
289
293
  functionName.includes('WithTransaction') ||
290
- (node.parent.id.typeAnnotation?.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
291
- node.parent.id.typeAnnotation.typeAnnotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
294
+ (node.parent.id.typeAnnotation?.typeAnnotation.type ===
295
+ utils_1.AST_NODE_TYPES.TSTypeReference &&
296
+ node.parent.id.typeAnnotation.typeAnnotation.typeName.type ===
297
+ utils_1.AST_NODE_TYPES.Identifier &&
292
298
  (node.parent.id.typeAnnotation.typeAnnotation.typeName.name.includes('Transaction') ||
293
299
  node.parent.id.typeAnnotation.typeAnnotation.typeName.name.includes('WithTransaction')))) {
294
300
  return true;
@@ -306,16 +312,18 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
306
312
  context.report({
307
313
  node,
308
314
  messageId: 'tooManyParams',
309
- data: { count: params.length },
315
+ data: { count: params.length, minimum: minParams },
310
316
  });
311
317
  return;
312
318
  }
313
319
  // Then check for same type parameters if enabled
314
320
  if (finalOptions.checkSameTypeParameters && params.length >= 2) {
315
- if (hasSameTypeParameters(params)) {
321
+ const duplicateType = findDuplicateParameterType(params);
322
+ if (duplicateType) {
316
323
  context.report({
317
324
  node,
318
325
  messageId: 'sameTypeParams',
326
+ data: { type: duplicateType, paramCount: params.length },
319
327
  });
320
328
  }
321
329
  }
@@ -0,0 +1,3 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export type MessageIds = 'preferTypeAlias' | 'defineTypeBeforeConstant';
3
+ export declare const preferTypeAliasOverTypeofConstant: TSESLint.RuleModule<MessageIds, never[]>;
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preferTypeAliasOverTypeofConstant = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const PAREN_TYPE = utils_1.AST_NODE_TYPES.TSParenthesizedType ??
7
+ 'TSParenthesizedType';
8
+ const isParenthesizedType = (node) => {
9
+ return node.type === PAREN_TYPE;
10
+ };
11
+ /** Utility to convert CONSTANT_CASE or snake/kebab to PascalCase */
12
+ function toPascalCase(name) {
13
+ return name
14
+ .split(/[_-]+/)
15
+ .filter(Boolean)
16
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
17
+ .join('');
18
+ }
19
+ /** Determine if a value initializer represents a function or class (which we should ignore). */
20
+ function isFunctionOrClassInitializer(init) {
21
+ if (!init)
22
+ return false;
23
+ return (init.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
24
+ init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
25
+ init.type === utils_1.AST_NODE_TYPES.ClassExpression);
26
+ }
27
+ /** Determine if node is a simple constant literal or object/array literal possibly with `as const` */
28
+ function isConstantLikeInitializer(init) {
29
+ if (!init)
30
+ return false;
31
+ if (isFunctionOrClassInitializer(init))
32
+ return false;
33
+ switch (init.type) {
34
+ case utils_1.AST_NODE_TYPES.Literal:
35
+ return true;
36
+ case utils_1.AST_NODE_TYPES.TemplateLiteral:
37
+ return init.expressions.length === 0;
38
+ case utils_1.AST_NODE_TYPES.ObjectExpression:
39
+ return true;
40
+ case utils_1.AST_NODE_TYPES.ArrayExpression:
41
+ return true;
42
+ case utils_1.AST_NODE_TYPES.TSAsExpression:
43
+ return isConstantLikeInitializer(init.expression);
44
+ case utils_1.AST_NODE_TYPES.UnaryExpression: {
45
+ // treat unary constants (e.g., -1) as constant-like; exclude typeof
46
+ if (init.operator === 'typeof')
47
+ return false;
48
+ return isConstantLikeInitializer(init.argument);
49
+ }
50
+ default:
51
+ return false;
52
+ }
53
+ }
54
+ /** Traverse a TSType and collect referenced identifier names (e.g., A, B in A | B). */
55
+ function collectReferencedTypeNames(node, acc = new Set()) {
56
+ switch (node.type) {
57
+ case utils_1.AST_NODE_TYPES.TSTypeReference: {
58
+ if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
59
+ acc.add(node.typeName.name);
60
+ }
61
+ if (node.typeParameters) {
62
+ for (const p of node.typeParameters.params)
63
+ collectReferencedTypeNames(p, acc);
64
+ }
65
+ break;
66
+ }
67
+ case utils_1.AST_NODE_TYPES.TSUnionType:
68
+ case utils_1.AST_NODE_TYPES.TSIntersectionType: {
69
+ for (const t of node.types)
70
+ collectReferencedTypeNames(t, acc);
71
+ break;
72
+ }
73
+ case utils_1.AST_NODE_TYPES.TSArrayType: {
74
+ const arr = node;
75
+ collectReferencedTypeNames(arr.elementType, acc);
76
+ break;
77
+ }
78
+ case utils_1.AST_NODE_TYPES.TSTypeOperator: {
79
+ const op = node;
80
+ if (op.typeAnnotation)
81
+ collectReferencedTypeNames(op.typeAnnotation, acc);
82
+ break;
83
+ }
84
+ case utils_1.AST_NODE_TYPES.TSTupleType: {
85
+ const tup = node;
86
+ for (const e of tup.elementTypes)
87
+ collectReferencedTypeNames(e, acc);
88
+ break;
89
+ }
90
+ default: {
91
+ if (isParenthesizedType(node)) {
92
+ collectReferencedTypeNames(node.typeAnnotation, acc);
93
+ }
94
+ break;
95
+ }
96
+ }
97
+ return acc;
98
+ }
99
+ /** Collects module-level consts and type aliases for quick lookup */
100
+ function collectTopLevelContext(program) {
101
+ const topLevelConstInitByName = new Map();
102
+ const topLevelConstNodeByName = new Map();
103
+ const typeAliasByName = new Map();
104
+ const importedValueNames = new Set();
105
+ const importedTypeNames = new Set();
106
+ for (const stmt of program.body) {
107
+ if (stmt.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
108
+ const isTypeImport = stmt.importKind === 'type';
109
+ for (const spec of stmt.specifiers) {
110
+ // import type { Foo } from '...'
111
+ // import { Foo } from '...' as value import
112
+ if (spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
113
+ if (isTypeImport || spec.importKind === 'type') {
114
+ importedTypeNames.add(spec.local.name);
115
+ }
116
+ else {
117
+ importedValueNames.add(spec.local.name);
118
+ }
119
+ }
120
+ else if (spec.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier) {
121
+ if (isTypeImport) {
122
+ importedTypeNames.add(spec.local.name);
123
+ }
124
+ else {
125
+ importedValueNames.add(spec.local.name);
126
+ }
127
+ }
128
+ else if (spec.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
129
+ // Namespace imports expose names via MemberExpression; not tracked individually
130
+ }
131
+ }
132
+ }
133
+ else if (stmt.type === utils_1.AST_NODE_TYPES.VariableDeclaration &&
134
+ stmt.kind === 'const') {
135
+ for (const decl of stmt.declarations) {
136
+ if (decl.id.type === utils_1.AST_NODE_TYPES.Identifier) {
137
+ topLevelConstInitByName.set(decl.id.name, decl.init ?? null);
138
+ topLevelConstNodeByName.set(decl.id.name, decl);
139
+ }
140
+ }
141
+ }
142
+ else if (stmt.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
143
+ typeAliasByName.set(stmt.id.name, stmt);
144
+ }
145
+ else if (stmt.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
146
+ stmt.declaration) {
147
+ // Handle exported declarations: export const FOO = ...; export type Foo = ...
148
+ const decl = stmt.declaration;
149
+ if (decl.type === utils_1.AST_NODE_TYPES.VariableDeclaration &&
150
+ decl.kind === 'const') {
151
+ for (const d of decl.declarations) {
152
+ if (d.id.type === utils_1.AST_NODE_TYPES.Identifier) {
153
+ topLevelConstInitByName.set(d.id.name, d.init ?? null);
154
+ topLevelConstNodeByName.set(d.id.name, d);
155
+ }
156
+ }
157
+ }
158
+ else if (decl.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
159
+ typeAliasByName.set(decl.id.name, decl);
160
+ }
161
+ }
162
+ }
163
+ return {
164
+ topLevelConstInitByName,
165
+ topLevelConstNodeByName,
166
+ typeAliasByName,
167
+ importedValueNames,
168
+ importedTypeNames,
169
+ };
170
+ }
171
+ exports.preferTypeAliasOverTypeofConstant = (0, createRule_1.createRule)({
172
+ name: 'prefer-type-alias-over-typeof-constant',
173
+ meta: {
174
+ type: 'suggestion',
175
+ docs: {
176
+ description: 'Prefer named type aliases over `typeof` on same-file global constants; ensure types are declared before constants.',
177
+ recommended: 'error',
178
+ },
179
+ hasSuggestions: false,
180
+ schema: [],
181
+ messages: {
182
+ preferTypeAlias: 'Type derived from same-file constant "{{constName}}" couples the type to its runtime value and scatters literal unions across the file. Create a named alias such as "{{suggested}}" and reference that alias instead of `typeof {{constName}}` so the type stays stable even if the value changes.',
183
+ defineTypeBeforeConstant: 'Type alias "{{typeName}}" appears after constant "{{constName}}", which hides the shape from readers and risks using an undeclared alias. Declare "{{typeName}}" before "{{constName}}" so the type is visible where it is consumed and can be reused consistently.',
184
+ },
185
+ },
186
+ defaultOptions: [],
187
+ create(context) {
188
+ let collected;
189
+ return {
190
+ Program(program) {
191
+ collected = collectTopLevelContext(program);
192
+ },
193
+ // Enforce that type aliases referenced by const annotations appear before the const
194
+ VariableDeclarator(node) {
195
+ if (!collected)
196
+ return;
197
+ if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier)
198
+ return;
199
+ const constName = node.id.name;
200
+ // Only for top-level consts
201
+ const decl = collected.topLevelConstNodeByName.get(constName);
202
+ if (!decl || decl !== node)
203
+ return;
204
+ const parentDecl = node.parent;
205
+ if (!parentDecl || parentDecl.kind !== 'const')
206
+ return;
207
+ const typeAnn = node.id.typeAnnotation?.typeAnnotation;
208
+ if (!typeAnn)
209
+ return;
210
+ for (const typeName of collectReferencedTypeNames(typeAnn)) {
211
+ // If imported type, allow any order
212
+ if (collected.importedTypeNames.has(typeName))
213
+ continue;
214
+ const typeDecl = collected.typeAliasByName.get(typeName);
215
+ if (typeDecl && typeDecl.range[0] > parentDecl.range[0]) {
216
+ context.report({
217
+ node: node.id,
218
+ messageId: 'defineTypeBeforeConstant',
219
+ data: { typeName, constName },
220
+ });
221
+ }
222
+ }
223
+ },
224
+ // Core rule: flag `typeof CONST` when CONST is a same-file top-level const and constant-like
225
+ TSTypeQuery(node) {
226
+ if (!collected)
227
+ return;
228
+ // Skip `keyof typeof X`
229
+ if (node.parent &&
230
+ node.parent.type === utils_1.AST_NODE_TYPES.TSTypeOperator &&
231
+ node.parent.operator === 'keyof') {
232
+ return;
233
+ }
234
+ if (node.exprName.type !== utils_1.AST_NODE_TYPES.Identifier) {
235
+ return;
236
+ }
237
+ const name = node.exprName.name;
238
+ // Imported values: do not enforce (may suggest in future)
239
+ if (collected.importedValueNames.has(name)) {
240
+ return;
241
+ }
242
+ const init = collected.topLevelConstInitByName.get(name);
243
+ if (init === undefined) {
244
+ // Not a top-level const in this file
245
+ return;
246
+ }
247
+ if (!isConstantLikeInitializer(init)) {
248
+ return;
249
+ }
250
+ context.report({
251
+ node,
252
+ messageId: 'preferTypeAlias',
253
+ data: { constName: name, suggested: toPascalCase(name) },
254
+ });
255
+ },
256
+ };
257
+ },
258
+ });
259
+ //# sourceMappingURL=prefer-type-alias-over-typeof-constant.js.map
@@ -12,7 +12,9 @@ exports.preferTypeOverInterface = (0, createRule_1.createRule)({
12
12
  },
13
13
  schema: [],
14
14
  messages: {
15
- preferType: 'Prefer using type alias over interface.',
15
+ preferType: 'Interface "{{interfaceName}}" should be declared as a type alias. ' +
16
+ 'Interfaces can merge across declarations and extend in chains, which fragments the resulting shape across files and makes composition harder to predict and trace. ' +
17
+ 'Replace `interface` with `type` and use intersections (for example, `type {{interfaceName}} = Base & { field: string }`) to keep the contract closed and predictable.',
16
18
  },
17
19
  fixable: 'code',
18
20
  },
@@ -23,8 +25,11 @@ exports.preferTypeOverInterface = (0, createRule_1.createRule)({
23
25
  context.report({
24
26
  node,
25
27
  messageId: 'preferType',
28
+ data: {
29
+ interfaceName: node.id.name,
30
+ },
26
31
  fix(fixer) {
27
- const sourceCode = context.getSourceCode();
32
+ const sourceCode = context.sourceCode;
28
33
  const openingBrace = sourceCode.getTokenAfter(node.id, {
29
34
  filter: (token) => token.value === '{',
30
35
  });
@@ -0,0 +1,2 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const preferUrlToStringOverToJson: TSESLint.RuleModule<'preferToString', never[]>;