@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,11 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ type Options = [
3
+ {
4
+ objectNamePattern?: string[];
5
+ ignoreInLoops?: boolean;
6
+ emptyCheckFunctions?: string[];
7
+ }
8
+ ];
9
+ type MessageIds = 'missingEmptyObjectCheck';
10
+ export declare const enforceEmptyObjectCheck: TSESLint.RuleModule<MessageIds, Options>;
11
+ export {};
@@ -0,0 +1,502 @@
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.enforceEmptyObjectCheck = 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 DEFAULT_OBJECT_SUFFIXES = [
31
+ 'Config',
32
+ 'Configs',
33
+ 'Data',
34
+ 'Info',
35
+ 'Settings',
36
+ 'Options',
37
+ 'Props',
38
+ 'State',
39
+ 'Response',
40
+ 'Result',
41
+ 'Payload',
42
+ 'Map',
43
+ 'Record',
44
+ 'Object',
45
+ 'Obj',
46
+ 'Details',
47
+ 'Meta',
48
+ 'Profile',
49
+ 'Request',
50
+ 'Params',
51
+ 'Context',
52
+ ];
53
+ const DEFAULT_EMPTY_CHECK_FUNCTIONS = ['isEmpty'];
54
+ const BOOLEAN_PREFIXES = [
55
+ 'is',
56
+ 'has',
57
+ 'can',
58
+ 'should',
59
+ 'was',
60
+ 'were',
61
+ 'will',
62
+ 'did',
63
+ ];
64
+ const NON_OBJECT_LIKE_NAMES = [
65
+ 'count',
66
+ 'index',
67
+ 'idx',
68
+ 'length',
69
+ 'size',
70
+ 'total',
71
+ 'flag',
72
+ 'enabled',
73
+ 'ready',
74
+ 'items',
75
+ 'item',
76
+ 'list',
77
+ 'lists',
78
+ 'array',
79
+ 'arr',
80
+ ];
81
+ function hasBooleanPrefixBoundary(name) {
82
+ const lower = name.toLowerCase();
83
+ return BOOLEAN_PREFIXES.some((prefix) => {
84
+ if (!lower.startsWith(prefix)) {
85
+ return false;
86
+ }
87
+ const boundary = name.charAt(prefix.length);
88
+ return boundary !== '' && boundary >= 'A' && boundary <= 'Z';
89
+ });
90
+ }
91
+ function isLoopLike(node) {
92
+ return (node.type === utils_1.AST_NODE_TYPES.ForStatement ||
93
+ node.type === utils_1.AST_NODE_TYPES.ForInStatement ||
94
+ node.type === utils_1.AST_NODE_TYPES.ForOfStatement ||
95
+ node.type === utils_1.AST_NODE_TYPES.WhileStatement ||
96
+ node.type === utils_1.AST_NODE_TYPES.DoWhileStatement);
97
+ }
98
+ function isInsideLoop(node) {
99
+ let current = node;
100
+ while (current && current.parent) {
101
+ if (isLoopLike(current.parent)) {
102
+ return true;
103
+ }
104
+ current = current.parent;
105
+ }
106
+ return false;
107
+ }
108
+ function isObjectLikeName(name, patterns) {
109
+ const lower = name.toLowerCase();
110
+ if (hasBooleanPrefixBoundary(name)) {
111
+ return false;
112
+ }
113
+ if (NON_OBJECT_LIKE_NAMES.includes(lower)) {
114
+ return false;
115
+ }
116
+ for (const pattern of patterns) {
117
+ if (name.endsWith(pattern) || lower.endsWith(pattern.toLowerCase())) {
118
+ return true;
119
+ }
120
+ }
121
+ return false;
122
+ }
123
+ function isNullableType(type) {
124
+ return ((type.flags & ts.TypeFlags.Null) !== 0 ||
125
+ (type.flags & ts.TypeFlags.Undefined) !== 0 ||
126
+ (type.flags & ts.TypeFlags.Void) !== 0);
127
+ }
128
+ function isNonObjectPrimitive(type) {
129
+ const flag = type.flags;
130
+ return ((flag & ts.TypeFlags.StringLike) !== 0 ||
131
+ (flag & ts.TypeFlags.NumberLike) !== 0 ||
132
+ (flag & ts.TypeFlags.BooleanLike) !== 0 ||
133
+ (flag & ts.TypeFlags.BigIntLike) !== 0 ||
134
+ (flag & ts.TypeFlags.ESSymbolLike) !== 0 ||
135
+ (flag & ts.TypeFlags.EnumLike) !== 0);
136
+ }
137
+ function isAnyOrUnknown(type) {
138
+ return ((type.flags & ts.TypeFlags.Any) !== 0 ||
139
+ (type.flags & ts.TypeFlags.Unknown) !== 0);
140
+ }
141
+ function hasRequiredProperties(type, checker) {
142
+ const properties = checker.getPropertiesOfType(type);
143
+ return properties.some((property) => (property.getFlags() & ts.SymbolFlags.Optional) === 0);
144
+ }
145
+ function isObjectLikeType(type, checker) {
146
+ if (type.isUnion()) {
147
+ let hasObject = false;
148
+ let hasUnknown = false;
149
+ let hasNonObject = false;
150
+ let hasNonNullable = false;
151
+ for (const part of type.types) {
152
+ if (isNullableType(part)) {
153
+ continue;
154
+ }
155
+ hasNonNullable = true;
156
+ if (isNonObjectPrimitive(part)) {
157
+ hasNonObject = true;
158
+ continue;
159
+ }
160
+ const analysis = isObjectLikeType(part, checker);
161
+ if (analysis === 'object') {
162
+ hasObject = true;
163
+ }
164
+ else if (analysis === 'unknown') {
165
+ hasUnknown = true;
166
+ }
167
+ else {
168
+ hasNonObject = true;
169
+ }
170
+ }
171
+ if (hasObject) {
172
+ return 'object';
173
+ }
174
+ if (!hasNonNullable) {
175
+ return 'non-object';
176
+ }
177
+ if (hasUnknown) {
178
+ return 'unknown';
179
+ }
180
+ return hasNonObject ? 'non-object' : 'unknown';
181
+ }
182
+ if ((type.flags & ts.TypeFlags.Intersection) !== 0) {
183
+ const intersectionType = type;
184
+ let hasObject = false;
185
+ let hasUnknown = false;
186
+ for (const part of intersectionType.types) {
187
+ const analysis = isObjectLikeType(part, checker);
188
+ if (analysis === 'non-object') {
189
+ return 'non-object';
190
+ }
191
+ if (analysis === 'object') {
192
+ hasObject = true;
193
+ }
194
+ else {
195
+ hasUnknown = true;
196
+ }
197
+ }
198
+ if (hasObject) {
199
+ return 'object';
200
+ }
201
+ return hasUnknown ? 'unknown' : 'non-object';
202
+ }
203
+ if (isAnyOrUnknown(type)) {
204
+ return 'unknown';
205
+ }
206
+ if (isNonObjectPrimitive(type) || isNullableType(type)) {
207
+ return 'non-object';
208
+ }
209
+ if ((type.flags & ts.TypeFlags.Object) === 0) {
210
+ return 'non-object';
211
+ }
212
+ if (checker.isArrayType(type) || checker.isTupleType(type)) {
213
+ return 'non-object';
214
+ }
215
+ if (type.getCallSignatures().length > 0) {
216
+ return 'non-object';
217
+ }
218
+ if (hasRequiredProperties(type, checker)) {
219
+ return 'non-object';
220
+ }
221
+ return 'object';
222
+ }
223
+ function isObjectKeysLengthExpression(node, name) {
224
+ if (node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
225
+ !node.computed &&
226
+ node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
227
+ node.property.name === 'length' &&
228
+ node.object.type === utils_1.AST_NODE_TYPES.CallExpression &&
229
+ node.object.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
230
+ !node.object.callee.computed &&
231
+ node.object.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
232
+ node.object.callee.object.name === 'Object' &&
233
+ node.object.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
234
+ node.object.callee.property.name === 'keys' &&
235
+ node.object.arguments.length === 1 &&
236
+ node.object.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier &&
237
+ node.object.arguments[0].name === name) {
238
+ return true;
239
+ }
240
+ return false;
241
+ }
242
+ function isZeroLiteral(node) {
243
+ return node.type === utils_1.AST_NODE_TYPES.Literal && node.value === 0;
244
+ }
245
+ function isLengthZeroComparison(node, name) {
246
+ const { operator, left, right } = node;
247
+ const leftIsLength = isObjectKeysLengthExpression(left, name);
248
+ const rightIsLength = isObjectKeysLengthExpression(right, name);
249
+ const leftIsZero = isZeroLiteral(left);
250
+ const rightIsZero = isZeroLiteral(right);
251
+ /**
252
+ * Only zero-length checks signal emptiness: length never drops below zero, and
253
+ * `> 0` means data is present. Restrict operators to equality and zero-bound
254
+ * comparisons to avoid mistaking impossible `length < 0` or presence checks for
255
+ * valid emptiness guards.
256
+ */
257
+ if (operator === '===' || operator === '==') {
258
+ return (leftIsLength && rightIsZero) || (rightIsLength && leftIsZero);
259
+ }
260
+ if (operator === '<=') {
261
+ return leftIsLength && rightIsZero;
262
+ }
263
+ if (operator === '>=') {
264
+ return leftIsZero && rightIsLength;
265
+ }
266
+ return false;
267
+ }
268
+ function conditionHasEmptyCheck(node, name, emptyCheckFunctions, negationDepth = 0) {
269
+ if (!node)
270
+ return false;
271
+ switch (node.type) {
272
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
273
+ return (conditionHasEmptyCheck(node.left, name, emptyCheckFunctions, negationDepth) ||
274
+ conditionHasEmptyCheck(node.right, name, emptyCheckFunctions, negationDepth));
275
+ case utils_1.AST_NODE_TYPES.BinaryExpression:
276
+ if (isLengthZeroComparison(node, name)) {
277
+ return negationDepth % 2 === 0;
278
+ }
279
+ return (conditionHasEmptyCheck(node.left, name, emptyCheckFunctions, negationDepth) ||
280
+ conditionHasEmptyCheck(node.right, name, emptyCheckFunctions, negationDepth));
281
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
282
+ if (node.operator === '!') {
283
+ return conditionHasEmptyCheck(node.argument, name, emptyCheckFunctions, negationDepth + 1);
284
+ }
285
+ return conditionHasEmptyCheck(node.argument, name, emptyCheckFunctions, negationDepth);
286
+ case utils_1.AST_NODE_TYPES.CallExpression: {
287
+ const callee = node.callee;
288
+ const firstArgIsTarget = node.arguments[0] &&
289
+ node.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier &&
290
+ node.arguments[0].name === name;
291
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
292
+ emptyCheckFunctions.has(callee.name) &&
293
+ firstArgIsTarget) {
294
+ return negationDepth % 2 === 0;
295
+ }
296
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
297
+ !callee.computed &&
298
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
299
+ emptyCheckFunctions.has(callee.property.name) &&
300
+ firstArgIsTarget) {
301
+ return negationDepth % 2 === 0;
302
+ }
303
+ return (conditionHasEmptyCheck(callee, name, emptyCheckFunctions, negationDepth) ||
304
+ node.arguments.some((argument) => conditionHasEmptyCheck(argument, name, emptyCheckFunctions, negationDepth)));
305
+ }
306
+ case utils_1.AST_NODE_TYPES.MemberExpression:
307
+ /**
308
+ * `!Object.keys(name).length` counts as an emptiness check through negation
309
+ * depth; comparisons like `length < 0` or `length > 0` remain excluded because
310
+ * length is never negative and `> 0` signals presence rather than emptiness.
311
+ */
312
+ if (isObjectKeysLengthExpression(node, name)) {
313
+ return negationDepth % 2 === 1;
314
+ }
315
+ return conditionHasEmptyCheck(node.object, name, emptyCheckFunctions, negationDepth);
316
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
317
+ return (conditionHasEmptyCheck(node.test, name, emptyCheckFunctions, negationDepth) ||
318
+ conditionHasEmptyCheck(node.consequent, name, emptyCheckFunctions, negationDepth) ||
319
+ conditionHasEmptyCheck(node.alternate, name, emptyCheckFunctions, negationDepth));
320
+ default:
321
+ return false;
322
+ }
323
+ }
324
+ function collectNegations(node, results) {
325
+ if (node.type === utils_1.AST_NODE_TYPES.UnaryExpression && node.operator === '!') {
326
+ results.push(node);
327
+ }
328
+ else if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
329
+ collectNegations(node.left, results);
330
+ collectNegations(node.right, results);
331
+ }
332
+ else if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
333
+ collectNegations(node.test, results);
334
+ collectNegations(node.consequent, results);
335
+ collectNegations(node.alternate, results);
336
+ }
337
+ }
338
+ function getRootCondition(node) {
339
+ let current = node;
340
+ while (current && current.parent) {
341
+ const parent = current.parent;
342
+ if ((parent.type === utils_1.AST_NODE_TYPES.IfStatement && parent.test === current) ||
343
+ (parent.type === utils_1.AST_NODE_TYPES.WhileStatement &&
344
+ parent.test === current) ||
345
+ (parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement &&
346
+ parent.test === current) ||
347
+ (parent.type === utils_1.AST_NODE_TYPES.ForStatement &&
348
+ parent.test === current) ||
349
+ (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
350
+ parent.test === current)) {
351
+ return current;
352
+ }
353
+ if (parent.type === utils_1.AST_NODE_TYPES.LogicalExpression ||
354
+ parent.type === utils_1.AST_NODE_TYPES.BinaryExpression ||
355
+ parent.type === utils_1.AST_NODE_TYPES.UnaryExpression ||
356
+ parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
357
+ current = parent;
358
+ continue;
359
+ }
360
+ break;
361
+ }
362
+ return null;
363
+ }
364
+ exports.enforceEmptyObjectCheck = (0, createRule_1.createRule)({
365
+ name: 'enforce-empty-object-check',
366
+ meta: {
367
+ type: 'problem',
368
+ docs: {
369
+ description: 'Ensure object existence checks also guard against empty objects so that empty payloads are treated like missing data.',
370
+ recommended: 'error',
371
+ },
372
+ fixable: 'code',
373
+ schema: [
374
+ {
375
+ type: 'object',
376
+ properties: {
377
+ objectNamePattern: {
378
+ type: 'array',
379
+ items: { type: 'string' },
380
+ },
381
+ ignoreInLoops: {
382
+ type: 'boolean',
383
+ },
384
+ emptyCheckFunctions: {
385
+ type: 'array',
386
+ items: { type: 'string' },
387
+ },
388
+ },
389
+ additionalProperties: false,
390
+ },
391
+ ],
392
+ messages: {
393
+ missingEmptyObjectCheck: 'What\'s wrong: "{{name}}" is only checked for falsiness, so `{}` passes the guard. Why it matters: empty payloads or configs behave like missing data and can execute "has data" logic incorrectly. How to fix: also check emptiness (for example, Object.keys({{name}}).length === 0 or a configured empty-check helper).',
394
+ },
395
+ },
396
+ defaultOptions: [{}],
397
+ create(context) {
398
+ const sourceCode = context.getSourceCode();
399
+ const parserServices = sourceCode.parserServices;
400
+ const checker = parserServices?.program?.getTypeChecker();
401
+ const options = context.options[0] ?? {};
402
+ const { objectNamePattern = [], ignoreInLoops = false, emptyCheckFunctions = [], } = options;
403
+ const patternSet = new Set([
404
+ ...DEFAULT_OBJECT_SUFFIXES,
405
+ ...objectNamePattern,
406
+ ]);
407
+ const emptyCheckFunctionsSet = new Set([
408
+ ...DEFAULT_EMPTY_CHECK_FUNCTIONS,
409
+ ...emptyCheckFunctions,
410
+ ]);
411
+ const processedExpressions = new WeakSet();
412
+ const processedNegations = new WeakSet();
413
+ function isLikelyObject(identifier) {
414
+ if (checker && parserServices?.esTreeNodeToTSNodeMap) {
415
+ try {
416
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(identifier);
417
+ const type = checker.getTypeAtLocation(tsNode);
418
+ const analysis = isObjectLikeType(type, checker);
419
+ if (analysis === 'object') {
420
+ return true;
421
+ }
422
+ if (analysis === 'non-object') {
423
+ return false;
424
+ }
425
+ }
426
+ catch {
427
+ // TypeScript parser services can throw when AST-to-TS node mapping fails; fall back to naming heuristic so linting does not crash.
428
+ }
429
+ }
430
+ return isObjectLikeName(identifier.name, patternSet);
431
+ }
432
+ function reportNegation(node, identifier) {
433
+ if (processedNegations.has(node)) {
434
+ return;
435
+ }
436
+ processedNegations.add(node);
437
+ if (ignoreInLoops && isInsideLoop(node)) {
438
+ return;
439
+ }
440
+ const conditionRoot = getRootCondition(node);
441
+ if (conditionRoot &&
442
+ conditionHasEmptyCheck(conditionRoot, identifier.name, emptyCheckFunctionsSet)) {
443
+ return;
444
+ }
445
+ if (!isLikelyObject(identifier)) {
446
+ return;
447
+ }
448
+ context.report({
449
+ node,
450
+ messageId: 'missingEmptyObjectCheck',
451
+ data: {
452
+ name: identifier.name,
453
+ },
454
+ fix(fixer) {
455
+ const identifierText = sourceCode.getText(identifier);
456
+ const replacement = `(${node.operator}${identifierText} || Object.keys(${identifierText}).length === 0)`;
457
+ return fixer.replaceText(node, replacement);
458
+ },
459
+ });
460
+ }
461
+ function handleTestExpression(expression) {
462
+ if (processedExpressions.has(expression)) {
463
+ return;
464
+ }
465
+ processedExpressions.add(expression);
466
+ const negations = [];
467
+ collectNegations(expression, negations);
468
+ for (const negation of negations) {
469
+ if (negation.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
470
+ negation.operator === '!') {
471
+ reportNegation(negation, negation.argument);
472
+ }
473
+ }
474
+ }
475
+ return {
476
+ IfStatement(node) {
477
+ if (node.test) {
478
+ handleTestExpression(node.test);
479
+ }
480
+ },
481
+ WhileStatement(node) {
482
+ if (node.test) {
483
+ handleTestExpression(node.test);
484
+ }
485
+ },
486
+ DoWhileStatement(node) {
487
+ if (node.test) {
488
+ handleTestExpression(node.test);
489
+ }
490
+ },
491
+ ForStatement(node) {
492
+ if (node.test) {
493
+ handleTestExpression(node.test);
494
+ }
495
+ },
496
+ ConditionalExpression(node) {
497
+ handleTestExpression(node.test);
498
+ },
499
+ };
500
+ },
501
+ });
502
+ //# sourceMappingURL=enforce-empty-object-check.js.map
@@ -14,9 +14,9 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
14
14
  },
15
15
  schema: [],
16
16
  messages: {
17
- missingExportedType: 'Type {{typeName}} should be exported since it is used in an exported function. Add `export` before the type definition: `export type {{typeName}} = ...`',
18
- missingExportedReturnType: 'Return type {{typeName}} should be exported since it is used in an exported function. Add `export` before the type definition: `export type {{typeName}} = ...`',
19
- missingExportedPropsType: 'Props type {{typeName}} should be exported since it is used in an exported React component. Add `export` before the type definition: `export type {{typeName}} = ...`',
17
+ missingExportedType: 'Type "{{typeName}}" is used in a parameter of an exported function but is not exported. Callers cannot import the parameter contract, which forces duplicate or ad-hoc types and makes the API drift. Export the type (e.g., `export type {{typeName}} = ...`) or reuse an already exported type.',
18
+ missingExportedReturnType: 'Return type "{{typeName}}" belongs to an exported function but is not exported. Consumers cannot reference the returned shape for validation or composition, which leads to duplicated types and diverging contracts. Export the return type alias or interface so callers can import the shared contract.',
19
+ missingExportedPropsType: 'Props type "{{typeName}}" is used by an exported React component but is not exported. Other modules cannot type the props when composing the component and end up recreating the shape. Export the props type (e.g., `export type {{typeName}} = ...`) or reference an existing exported props contract.',
20
20
  },
21
21
  },
22
22
  defaultOptions: [],
@@ -402,7 +402,7 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
402
402
  if (isBuiltInType(typeName)) {
403
403
  return true;
404
404
  }
405
- const sourceCode = context.getSourceCode();
405
+ const sourceCode = context.sourceCode;
406
406
  const program = sourceCode.ast;
407
407
  // Check for imported types first - if found, return true immediately
408
408
  // since imported types are already available to consumers
@@ -0,0 +1,2 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const enforceFieldPathSyntaxInDocSetter: TSESLint.RuleModule<"enforceFieldPathSyntax", [], TSESLint.RuleListener>;