@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.
- package/README.md +171 -99
- package/lib/index.js +300 -17
- package/lib/rules/array-methods-this-context.js +20 -2
- package/lib/rules/avoid-utils-directory.js +11 -2
- package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
- package/lib/rules/consistent-callback-naming.js +1 -1
- package/lib/rules/dynamic-https-errors.d.ts +3 -1
- package/lib/rules/dynamic-https-errors.js +48 -11
- package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
- package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +52 -96
- package/lib/rules/enforce-assert-throws.d.ts +3 -1
- package/lib/rules/enforce-assert-throws.js +493 -51
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
- package/lib/rules/enforce-boolean-naming-prefixes.js +820 -39
- package/lib/rules/enforce-callable-types.js +4 -4
- package/lib/rules/enforce-callback-memo.js +143 -18
- package/lib/rules/enforce-centralized-mock-firestore.js +5 -2
- package/lib/rules/enforce-console-error.d.ts +3 -0
- package/lib/rules/enforce-console-error.js +278 -0
- package/lib/rules/enforce-css-media-queries.js +24 -46
- package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
- package/lib/rules/enforce-dynamic-file-naming.js +53 -28
- package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
- package/lib/rules/enforce-dynamic-imports.js +1 -1
- package/lib/rules/enforce-early-destructuring.d.ts +2 -0
- package/lib/rules/enforce-early-destructuring.js +980 -0
- package/lib/rules/enforce-empty-object-check.d.ts +11 -0
- package/lib/rules/enforce-empty-object-check.js +502 -0
- package/lib/rules/enforce-exported-function-types.js +4 -4
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +262 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.js +350 -3
- package/lib/rules/enforce-firestore-facade.js +349 -157
- package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
- package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
- package/lib/rules/enforce-firestore-set-merge.js +3 -4
- package/lib/rules/enforce-global-constants.d.ts +4 -1
- package/lib/rules/enforce-global-constants.js +240 -12
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.d.ts +2 -1
- package/lib/rules/enforce-memoize-async.js +92 -27
- package/lib/rules/enforce-memoize-getters.d.ts +3 -0
- package/lib/rules/enforce-memoize-getters.js +147 -0
- package/lib/rules/enforce-microdiff.js +3 -2
- package/lib/rules/enforce-object-literal-as-const.js +4 -4
- package/lib/rules/enforce-positive-naming.js +9 -7
- package/lib/rules/enforce-props-argument-name.d.ts +2 -7
- package/lib/rules/enforce-props-argument-name.js +165 -151
- package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
- package/lib/rules/enforce-props-naming-consistency.js +145 -0
- package/lib/rules/enforce-querykey-ts.d.ts +9 -0
- package/lib/rules/enforce-querykey-ts.js +274 -0
- package/lib/rules/enforce-safe-stringify.js +1 -1
- package/lib/rules/enforce-serializable-params.js +3 -3
- package/lib/rules/enforce-singular-type-names.js +10 -6
- package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
- package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
- package/lib/rules/enforce-storage-context.d.ts +9 -0
- package/lib/rules/enforce-storage-context.js +646 -0
- package/lib/rules/enforce-timestamp-now.js +23 -7
- package/lib/rules/enforce-transform-memoization.d.ts +4 -0
- package/lib/rules/enforce-transform-memoization.js +416 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.js +100 -0
- package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
- package/lib/rules/enforce-unique-cursor-headers.js +365 -0
- package/lib/rules/enforce-verb-noun-naming.js +90 -14
- package/lib/rules/ensure-pointer-events-none.js +28 -2
- package/lib/rules/export-if-in-doubt.js +27 -4
- package/lib/rules/extract-global-constants.js +92 -53
- package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
- package/lib/rules/fast-deep-equal-over-microdiff.js +351 -83
- package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
- package/lib/rules/firestore-transaction-reads-before-writes.js +277 -0
- package/lib/rules/flatten-push-calls.d.ts +2 -0
- package/lib/rules/flatten-push-calls.js +428 -0
- package/lib/rules/generic-starts-with-t.js +8 -2
- package/lib/rules/global-const-style.js +50 -19
- package/lib/rules/jsdoc-above-field.d.ts +11 -0
- package/lib/rules/jsdoc-above-field.js +208 -0
- package/lib/rules/key-only-outermost-element.js +21 -3
- package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
- package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
- package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
- package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
- package/lib/rules/memo-nested-react-components.d.ts +8 -0
- package/lib/rules/memo-nested-react-components.js +410 -0
- package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
- package/lib/rules/memoize-root-level-hocs.js +242 -0
- package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
- package/lib/rules/no-always-true-false-conditions.js +68 -8
- package/lib/rules/no-array-length-in-deps.d.ts +12 -0
- package/lib/rules/no-array-length-in-deps.js +314 -0
- package/lib/rules/no-async-array-filter.js +5 -2
- package/lib/rules/no-async-foreach.js +210 -12
- package/lib/rules/no-circular-references.js +18 -17
- package/lib/rules/no-class-instance-destructuring.js +55 -12
- package/lib/rules/no-complex-cloud-params.js +22 -5
- package/lib/rules/no-compositing-layer-props.js +6 -4
- package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
- package/lib/rules/no-console-error.d.ts +9 -0
- package/lib/rules/no-console-error.js +525 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
- package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
- package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
- package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
- package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
- package/lib/rules/no-entire-object-hook-deps.js +320 -68
- package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
- package/lib/rules/no-excessive-parent-chain.js +273 -0
- package/lib/rules/no-explicit-return-type.d.ts +2 -1
- package/lib/rules/no-explicit-return-type.js +215 -26
- package/lib/rules/no-filter-without-return.js +5 -1
- package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
- package/lib/rules/no-firestore-jest-mock.js +126 -8
- package/lib/rules/no-firestore-object-arrays.js +398 -28
- package/lib/rules/no-handler-suffix.d.ts +12 -0
- package/lib/rules/no-handler-suffix.js +305 -0
- package/lib/rules/no-hungarian.js +14 -14
- package/lib/rules/no-inline-component-prop.d.ts +10 -0
- package/lib/rules/no-inline-component-prop.js +456 -0
- package/lib/rules/no-jsx-in-hooks.js +6 -1
- package/lib/rules/no-jsx-whitespace-literal.js +8 -2
- package/lib/rules/no-margin-properties.d.ts +7 -1
- package/lib/rules/no-margin-properties.js +22 -9
- package/lib/rules/no-memoize-on-static.js +9 -1
- package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
- package/lib/rules/no-misleading-boolean-prefixes.js +331 -0
- package/lib/rules/no-misused-switch-case.js +22 -1
- package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
- package/lib/rules/no-mixed-firestore-transactions.js +296 -34
- package/lib/rules/no-mock-firebase-admin.js +5 -2
- package/lib/rules/no-object-values-on-strings.js +10 -2
- package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.js +272 -0
- package/lib/rules/no-passthrough-getters.d.ts +4 -0
- package/lib/rules/no-passthrough-getters.js +206 -0
- package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
- package/lib/rules/no-redundant-annotation-assertion.js +402 -0
- package/lib/rules/no-redundant-param-types.js +16 -6
- package/lib/rules/no-redundant-this-params.d.ts +3 -0
- package/lib/rules/no-redundant-this-params.js +459 -0
- package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
- package/lib/rules/no-redundant-usecallback-wrapper.js +328 -0
- package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
- package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
- package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
- package/lib/rules/no-restricted-properties-fix.js +173 -0
- package/lib/rules/no-separate-loading-state.d.ts +8 -0
- package/lib/rules/no-separate-loading-state.js +133 -0
- package/lib/rules/no-stale-state-across-await.d.ts +1 -0
- package/lib/rules/no-stale-state-across-await.js +185 -0
- package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
- package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
- package/lib/rules/no-type-assertion-returns.js +49 -49
- package/lib/rules/no-undefined-null-passthrough.js +217 -40
- package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
- package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
- package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
- package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
- package/lib/rules/no-unnecessary-destructuring.js +14 -4
- package/lib/rules/no-unnecessary-verb-suffix.js +2 -3
- package/lib/rules/no-unpinned-dependencies.js +36 -5
- package/lib/rules/no-unsafe-firestore-spread.js +8 -5
- package/lib/rules/no-unused-props.d.ts +5 -2
- package/lib/rules/no-unused-props.js +451 -112
- package/lib/rules/no-unused-usestate.js +6 -2
- package/lib/rules/no-useless-fragment.js +28 -2
- package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
- package/lib/rules/no-useless-usememo-primitives.js +393 -0
- package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
- package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
- package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
- package/lib/rules/omit-index-html.d.ts +2 -1
- package/lib/rules/omit-index-html.js +62 -7
- package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
- package/lib/rules/optimize-object-boolean-conditions.js +236 -0
- package/lib/rules/parallelize-async-operations.d.ts +8 -0
- package/lib/rules/parallelize-async-operations.js +394 -0
- package/lib/rules/prefer-batch-operations.d.ts +1 -3
- package/lib/rules/prefer-batch-operations.js +228 -35
- package/lib/rules/prefer-block-comments-for-declarations.js +22 -7
- package/lib/rules/prefer-clone-deep.js +3 -3
- package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
- package/lib/rules/prefer-destructuring-no-class.js +244 -59
- package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
- package/lib/rules/prefer-docsetter-setall.js +243 -0
- package/lib/rules/prefer-document-flattening.d.ts +4 -0
- package/lib/rules/prefer-document-flattening.js +239 -0
- package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
- package/lib/rules/prefer-field-paths-in-transforms.js +251 -0
- package/lib/rules/prefer-fragment-component.js +4 -4
- package/lib/rules/prefer-fragment-shorthand.js +2 -1
- package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
- package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
- package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
- package/lib/rules/prefer-global-router-state-key.js +305 -31
- package/lib/rules/prefer-memoized-props.d.ts +3 -0
- package/lib/rules/prefer-memoized-props.js +445 -0
- package/lib/rules/prefer-next-dynamic.d.ts +9 -0
- package/lib/rules/prefer-next-dynamic.js +357 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +435 -0
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +7 -0
- package/lib/rules/prefer-nullish-coalescing-override.js +188 -0
- package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
- package/lib/rules/prefer-params-over-parent-id.js +277 -0
- package/lib/rules/prefer-settings-object.js +23 -15
- package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
- package/lib/rules/prefer-type-over-interface.js +7 -2
- package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
- package/lib/rules/prefer-url-tostring-over-tojson.js +169 -0
- package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
- package/lib/rules/prefer-use-deep-compare-memo.js +431 -0
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +25 -2
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
- package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
- package/lib/rules/prevent-children-clobber.d.ts +2 -0
- package/lib/rules/prevent-children-clobber.js +536 -0
- package/lib/rules/react-memoize-literals.d.ts +4 -0
- package/lib/rules/react-memoize-literals.js +495 -0
- package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
- package/lib/rules/react-usememo-should-be-component.js +144 -78
- package/lib/rules/require-dynamic-firebase-imports.js +2 -1
- package/lib/rules/require-hooks-default-params.js +17 -16
- package/lib/rules/require-https-error-cause.d.ts +4 -0
- package/lib/rules/require-https-error-cause.js +136 -0
- package/lib/rules/require-https-error.js +43 -21
- package/lib/rules/require-image-optimized.js +2 -2
- package/lib/rules/require-memo.js +12 -2
- package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
- package/lib/rules/require-memoize-jsx-returners.js +485 -0
- package/lib/rules/require-usememo-object-literals.js +9 -4
- package/lib/rules/semantic-function-prefixes.js +38 -18
- package/lib/rules/sync-onwrite-name-func.js +5 -1
- package/lib/rules/test-file-location-enforcement.d.ts +1 -0
- package/lib/rules/test-file-location-enforcement.js +57 -0
- package/lib/rules/use-custom-link.js +6 -1
- package/lib/rules/use-custom-router.js +6 -1
- package/lib/rules/use-latest-callback.d.ts +3 -0
- package/lib/rules/use-latest-callback.js +271 -0
- package/lib/rules/vertically-group-related-functions.d.ts +16 -0
- package/lib/rules/vertically-group-related-functions.js +480 -0
- package/lib/utils/ASTHelpers.d.ts +13 -1
- package/lib/utils/ASTHelpers.js +34 -0
- package/lib/utils/createRule.js +1 -1
- package/lib/utils/getMethodName.d.ts +27 -0
- package/lib/utils/getMethodName.js +35 -0
- package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
- package/lib/utils/ruleTester.d.ts +1 -0
- package/lib/utils/ruleTester.js +4 -1
- package/lib/utils/tsTypeClassifier.d.ts +30 -0
- package/lib/utils/tsTypeClassifier.js +149 -0
- package/package.json +23 -14
- package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noExcessiveParentChain = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
// Maximum number of consecutive .parent calls allowed before warning
|
|
7
|
+
const DEFAULT_MAX_PARENT_CHAIN_LENGTH = 2;
|
|
8
|
+
// Handler types that this rule applies to
|
|
9
|
+
const HANDLER_TYPES = new Set([
|
|
10
|
+
'DocumentChangeHandler',
|
|
11
|
+
'DocumentChangeHandlerTransaction',
|
|
12
|
+
'RealtimeDbChangeHandler',
|
|
13
|
+
'RealtimeDbChangeHandlerTransaction',
|
|
14
|
+
]);
|
|
15
|
+
exports.noExcessiveParentChain = (0, createRule_1.createRule)({
|
|
16
|
+
name: 'no-excessive-parent-chain',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'suggestion',
|
|
19
|
+
hasSuggestions: true,
|
|
20
|
+
docs: {
|
|
21
|
+
description: 'Discourage excessive use of the ref.parent property chain in Firestore and RealtimeDB change handlers',
|
|
22
|
+
recommended: 'error',
|
|
23
|
+
},
|
|
24
|
+
schema: [
|
|
25
|
+
{
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
max: {
|
|
29
|
+
type: 'integer',
|
|
30
|
+
minimum: 1,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
messages: {
|
|
37
|
+
excessiveParentChain: 'Found {{count}} consecutive ref.parent hops in this handler. Long parent chains break when Firestore/RealtimeDB paths change and bypass the typed params the trigger already provides. Read path components from event.params (for example, params.userId) instead of walking ref.parent repeatedly.',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultOptions: [{}],
|
|
41
|
+
create(context) {
|
|
42
|
+
const maxParentChainLength = context.options[0]?.max ?? DEFAULT_MAX_PARENT_CHAIN_LENGTH;
|
|
43
|
+
// Track variables that contain event data
|
|
44
|
+
const eventDataVariables = new Map();
|
|
45
|
+
const eventIdentifiers = new Set();
|
|
46
|
+
const HANDLER_PARAM_SOURCE = '__handler_param__';
|
|
47
|
+
const recordEventIdentifier = (name) => {
|
|
48
|
+
eventIdentifiers.add(name);
|
|
49
|
+
};
|
|
50
|
+
const getRootIdentifier = (node) => {
|
|
51
|
+
let current = node;
|
|
52
|
+
while (current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
53
|
+
current = current.object;
|
|
54
|
+
}
|
|
55
|
+
return current.type === utils_1.AST_NODE_TYPES.Identifier ? current.name : null;
|
|
56
|
+
};
|
|
57
|
+
const hasRefProperty = (node) => {
|
|
58
|
+
let current = node;
|
|
59
|
+
while (current) {
|
|
60
|
+
if (current.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
61
|
+
current.property.name === 'ref') {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
current =
|
|
65
|
+
current.object.type === utils_1.AST_NODE_TYPES.MemberExpression
|
|
66
|
+
? current.object
|
|
67
|
+
: null;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
const registerHandlerParams = (node) => {
|
|
72
|
+
for (const param of node.params) {
|
|
73
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
74
|
+
recordEventIdentifier(param.name);
|
|
75
|
+
}
|
|
76
|
+
if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
77
|
+
for (const prop of param.properties) {
|
|
78
|
+
if (prop.type === utils_1.AST_NODE_TYPES.Property &&
|
|
79
|
+
prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
80
|
+
prop.value.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
81
|
+
prop.key.name === 'data') {
|
|
82
|
+
eventDataVariables.set(prop.value.name, HANDLER_PARAM_SOURCE);
|
|
83
|
+
recordEventIdentifier(HANDLER_PARAM_SOURCE);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
// Check if a function is one of our handler types
|
|
90
|
+
function isHandlerFunction(node) {
|
|
91
|
+
if (node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
92
|
+
node.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
// Check if the function is assigned to a variable with a type annotation
|
|
96
|
+
const parent = node.parent;
|
|
97
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
98
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
99
|
+
// Check for type annotation
|
|
100
|
+
if (parent.id.typeAnnotation?.typeAnnotation) {
|
|
101
|
+
const typeNode = parent.id.typeAnnotation.typeAnnotation;
|
|
102
|
+
// Check if it's a reference to one of our handler types
|
|
103
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
104
|
+
typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
105
|
+
HANDLER_TYPES.has(typeNode.typeName.name)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Check for generic types that might be handlers
|
|
109
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
110
|
+
typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
111
|
+
// Try to resolve the type name to see if it's one of our handler types
|
|
112
|
+
const scope = context.getScope();
|
|
113
|
+
const typeName = typeNode.typeName.name;
|
|
114
|
+
const variable = scope.variables.find((v) => v.name === typeName);
|
|
115
|
+
if (variable && variable.defs.length > 0) {
|
|
116
|
+
const def = variable.defs[0];
|
|
117
|
+
if (def.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration &&
|
|
118
|
+
def.node.typeAnnotation.type ===
|
|
119
|
+
utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
120
|
+
def.node.typeAnnotation.typeName.type ===
|
|
121
|
+
utils_1.AST_NODE_TYPES.Identifier &&
|
|
122
|
+
HANDLER_TYPES.has(def.node.typeAnnotation.typeName.name)) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Check if the function is exported with a type annotation
|
|
130
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
|
|
131
|
+
parent.declaration?.type === utils_1.AST_NODE_TYPES.VariableDeclaration &&
|
|
132
|
+
parent.declaration.declarations[0]?.id.type ===
|
|
133
|
+
utils_1.AST_NODE_TYPES.Identifier &&
|
|
134
|
+
parent.declaration.declarations[0].id.typeAnnotation?.typeAnnotation) {
|
|
135
|
+
const typeNode = parent.declaration.declarations[0].id.typeAnnotation.typeAnnotation;
|
|
136
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
137
|
+
typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
138
|
+
HANDLER_TYPES.has(typeNode.typeName.name)) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
// Count consecutive .parent calls in a member expression chain
|
|
145
|
+
function countParentChain(node) {
|
|
146
|
+
let count = 1; // Start with 1 for the current .parent
|
|
147
|
+
let current = node.object;
|
|
148
|
+
// Traverse the chain of member expressions
|
|
149
|
+
while (current.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
150
|
+
current.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
151
|
+
current.property.name === 'parent') {
|
|
152
|
+
count++;
|
|
153
|
+
current = current.object;
|
|
154
|
+
}
|
|
155
|
+
return count;
|
|
156
|
+
}
|
|
157
|
+
// This function has been removed as it's no longer needed
|
|
158
|
+
return {
|
|
159
|
+
// Register handler parameters to capture destructured event data
|
|
160
|
+
'ArrowFunctionExpression, FunctionExpression'(node) {
|
|
161
|
+
if (isHandlerFunction(node)) {
|
|
162
|
+
registerHandlerParams(node);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
// Track variable assignments that contain event data
|
|
166
|
+
VariableDeclarator(node) {
|
|
167
|
+
if (node.id.type === utils_1.AST_NODE_TYPES.Identifier && node.init) {
|
|
168
|
+
// Track direct event.data assignments
|
|
169
|
+
if (node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
170
|
+
node.init.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
171
|
+
node.init.property.name === 'data' &&
|
|
172
|
+
node.init.object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
173
|
+
// Store the variable name and the source object (event)
|
|
174
|
+
eventDataVariables.set(node.id.name, node.init.object.name);
|
|
175
|
+
recordEventIdentifier(node.init.object.name);
|
|
176
|
+
}
|
|
177
|
+
// Track assignments from other tracked variables
|
|
178
|
+
if (node.init.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
179
|
+
eventDataVariables.has(node.init.name)) {
|
|
180
|
+
// Store the variable name with the same source as the original variable
|
|
181
|
+
eventDataVariables.set(node.id.name, eventDataVariables.get(node.init.name) || '');
|
|
182
|
+
const source = eventDataVariables.get(node.init.name);
|
|
183
|
+
if (source) {
|
|
184
|
+
recordEventIdentifier(source);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Track assignments from event data properties
|
|
188
|
+
if (node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
189
|
+
node.init.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
190
|
+
eventDataVariables.has(node.init.object.name)) {
|
|
191
|
+
// Store the variable name with the same source as the original variable
|
|
192
|
+
eventDataVariables.set(node.id.name, eventDataVariables.get(node.init.object.name) || '');
|
|
193
|
+
const source = eventDataVariables.get(node.init.object.name);
|
|
194
|
+
if (source) {
|
|
195
|
+
recordEventIdentifier(source);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Also track destructuring assignments
|
|
200
|
+
if (node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern &&
|
|
201
|
+
node.init?.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
202
|
+
const eventSource = eventDataVariables.get(node.init.name) ||
|
|
203
|
+
(eventIdentifiers.has(node.init.name) ? node.init.name : null);
|
|
204
|
+
for (const property of node.id.properties) {
|
|
205
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property &&
|
|
206
|
+
property.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
207
|
+
property.value.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
208
|
+
eventSource) {
|
|
209
|
+
const targetName = property.value.name;
|
|
210
|
+
eventDataVariables.set(targetName, eventSource);
|
|
211
|
+
recordEventIdentifier(eventSource);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
// Check for excessive parent chains in member expressions
|
|
217
|
+
MemberExpression(node) {
|
|
218
|
+
// Only check for .parent chains
|
|
219
|
+
if (node.property.type !== utils_1.AST_NODE_TYPES.Identifier ||
|
|
220
|
+
node.property.name !== 'parent') {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Count the number of consecutive .parent calls
|
|
224
|
+
const parentCount = countParentChain(node);
|
|
225
|
+
if (parentCount <= maxParentChainLength) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// Check if we're in a handler function
|
|
229
|
+
let current = node;
|
|
230
|
+
let inHandler = false;
|
|
231
|
+
while (current) {
|
|
232
|
+
if (current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
233
|
+
current.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
234
|
+
if (isHandlerFunction(current)) {
|
|
235
|
+
inHandler = true;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
current = current.parent;
|
|
240
|
+
}
|
|
241
|
+
if (!inHandler) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Only report when the chain originates from tracked event data and contains a ref segment
|
|
245
|
+
const rootIdentifier = getRootIdentifier(node);
|
|
246
|
+
if (!rootIdentifier) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!hasRefProperty(node) ||
|
|
250
|
+
(!eventDataVariables.has(rootIdentifier) &&
|
|
251
|
+
!eventIdentifiers.has(rootIdentifier))) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
context.report({
|
|
255
|
+
node,
|
|
256
|
+
messageId: 'excessiveParentChain',
|
|
257
|
+
data: {
|
|
258
|
+
count: parentCount,
|
|
259
|
+
},
|
|
260
|
+
suggest: [
|
|
261
|
+
{
|
|
262
|
+
messageId: 'excessiveParentChain',
|
|
263
|
+
fix(fixer) {
|
|
264
|
+
return fixer.replaceText(node, 'event.params');
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
//# sourceMappingURL=no-excessive-parent-chain.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type MessageIds = 'noExplicitReturnTypeInferable' | 'noExplicitReturnTypeNonInferable';
|
|
2
3
|
type Options = [
|
|
3
4
|
{
|
|
4
5
|
allowRecursiveFunctions?: boolean;
|
|
@@ -9,5 +10,5 @@ type Options = [
|
|
|
9
10
|
allowFirestoreFunctionFiles?: boolean;
|
|
10
11
|
}
|
|
11
12
|
];
|
|
12
|
-
export declare const noExplicitReturnType: TSESLint.RuleModule<
|
|
13
|
+
export declare const noExplicitReturnType: TSESLint.RuleModule<MessageIds, Options>;
|
|
13
14
|
export {};
|
|
@@ -11,6 +11,98 @@ const defaultOptions = {
|
|
|
11
11
|
allowDtsFiles: true,
|
|
12
12
|
allowFirestoreFunctionFiles: true,
|
|
13
13
|
};
|
|
14
|
+
function getNameFromIdentifierOrLiteral(key) {
|
|
15
|
+
if (key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
16
|
+
return key.name;
|
|
17
|
+
}
|
|
18
|
+
if (typeof key.value === 'string') {
|
|
19
|
+
return key.value;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function describeClassMethod(node) {
|
|
24
|
+
if (!node.computed &&
|
|
25
|
+
(node.key.type === utils_1.AST_NODE_TYPES.Identifier ||
|
|
26
|
+
(node.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
27
|
+
typeof node.key.value === 'string'))) {
|
|
28
|
+
const name = getNameFromIdentifierOrLiteral(node.key);
|
|
29
|
+
if (name) {
|
|
30
|
+
return `class method "${name}"`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return 'class method';
|
|
34
|
+
}
|
|
35
|
+
function describeMethodSignature(node) {
|
|
36
|
+
if (!node.computed &&
|
|
37
|
+
(node.key.type === utils_1.AST_NODE_TYPES.Identifier ||
|
|
38
|
+
(node.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
39
|
+
typeof node.key.value === 'string'))) {
|
|
40
|
+
const name = getNameFromIdentifierOrLiteral(node.key);
|
|
41
|
+
if (name) {
|
|
42
|
+
return `interface method "${name}"`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return 'interface method';
|
|
46
|
+
}
|
|
47
|
+
function describeFunctionDeclaration(node) {
|
|
48
|
+
if (node.id?.name) {
|
|
49
|
+
return `function "${node.id.name}"`;
|
|
50
|
+
}
|
|
51
|
+
return 'function';
|
|
52
|
+
}
|
|
53
|
+
function describeTSDeclareFunction(node) {
|
|
54
|
+
if (node.id?.name) {
|
|
55
|
+
return `function "${node.id.name}"`;
|
|
56
|
+
}
|
|
57
|
+
return 'function';
|
|
58
|
+
}
|
|
59
|
+
function describeFunctionExpression(node) {
|
|
60
|
+
if (node.id?.name) {
|
|
61
|
+
return `function "${node.id.name}"`;
|
|
62
|
+
}
|
|
63
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
64
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
65
|
+
return `function "${node.parent.id.name}"`;
|
|
66
|
+
}
|
|
67
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.Property &&
|
|
68
|
+
!node.parent.computed &&
|
|
69
|
+
(node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier ||
|
|
70
|
+
(node.parent.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
71
|
+
typeof node.parent.key.value === 'string'))) {
|
|
72
|
+
const name = getNameFromIdentifierOrLiteral(node.parent.key);
|
|
73
|
+
if (name) {
|
|
74
|
+
return `object method "${name}"`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return 'function expression';
|
|
78
|
+
}
|
|
79
|
+
function describeArrowFunction(node) {
|
|
80
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
81
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
82
|
+
return `arrow function "${node.parent.id.name}"`;
|
|
83
|
+
}
|
|
84
|
+
return 'arrow function';
|
|
85
|
+
}
|
|
86
|
+
function describeFunctionKind(node) {
|
|
87
|
+
switch (node.type) {
|
|
88
|
+
case utils_1.AST_NODE_TYPES.MethodDefinition:
|
|
89
|
+
return describeClassMethod(node);
|
|
90
|
+
case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
|
91
|
+
return describeClassMethod(node);
|
|
92
|
+
case utils_1.AST_NODE_TYPES.TSMethodSignature:
|
|
93
|
+
return describeMethodSignature(node);
|
|
94
|
+
case utils_1.AST_NODE_TYPES.TSDeclareFunction:
|
|
95
|
+
return describeTSDeclareFunction(node);
|
|
96
|
+
case utils_1.AST_NODE_TYPES.FunctionDeclaration:
|
|
97
|
+
return describeFunctionDeclaration(node);
|
|
98
|
+
case utils_1.AST_NODE_TYPES.FunctionExpression:
|
|
99
|
+
return describeFunctionExpression(node);
|
|
100
|
+
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
101
|
+
return describeArrowFunction(node);
|
|
102
|
+
default:
|
|
103
|
+
return 'function';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
14
106
|
function isRecursiveFunction(node) {
|
|
15
107
|
const functionName = node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration
|
|
16
108
|
? node.id?.name
|
|
@@ -63,16 +155,52 @@ function isOverloadedFunction(node) {
|
|
|
63
155
|
const interfaceBody = node.parent;
|
|
64
156
|
if (interfaceBody.type !== utils_1.AST_NODE_TYPES.TSInterfaceBody)
|
|
65
157
|
return false;
|
|
66
|
-
|
|
158
|
+
if (node.computed)
|
|
159
|
+
return false;
|
|
160
|
+
const methodName = node.key.type === utils_1.AST_NODE_TYPES.Identifier ||
|
|
161
|
+
node.key.type === utils_1.AST_NODE_TYPES.Literal
|
|
162
|
+
? getNameFromIdentifierOrLiteral(node.key)
|
|
163
|
+
: undefined;
|
|
67
164
|
if (!methodName)
|
|
68
165
|
return false;
|
|
69
166
|
return (interfaceBody.body.filter((member) => member.type === utils_1.AST_NODE_TYPES.TSMethodSignature &&
|
|
70
|
-
member.
|
|
71
|
-
member.key.
|
|
167
|
+
!member.computed &&
|
|
168
|
+
(member.key.type === utils_1.AST_NODE_TYPES.Identifier ||
|
|
169
|
+
member.key.type === utils_1.AST_NODE_TYPES.Literal) &&
|
|
170
|
+
getNameFromIdentifierOrLiteral(member.key) === methodName).length > 1);
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
function isOverloadedTsDeclareFunction(node) {
|
|
175
|
+
const functionName = node.id?.name;
|
|
176
|
+
if (!functionName)
|
|
177
|
+
return false;
|
|
178
|
+
let container = node.parent;
|
|
179
|
+
while (container) {
|
|
180
|
+
if (container.type === utils_1.AST_NODE_TYPES.Program ||
|
|
181
|
+
container.type === utils_1.AST_NODE_TYPES.TSModuleBlock) {
|
|
182
|
+
const declarations = container.body
|
|
183
|
+
.map((statement) => {
|
|
184
|
+
if (statement.type === utils_1.AST_NODE_TYPES.TSDeclareFunction) {
|
|
185
|
+
return statement;
|
|
186
|
+
}
|
|
187
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
|
|
188
|
+
statement.declaration?.type === utils_1.AST_NODE_TYPES.TSDeclareFunction) {
|
|
189
|
+
return statement.declaration;
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
})
|
|
193
|
+
.filter((value) => Boolean(value?.id?.name))
|
|
194
|
+
.filter((decl) => decl.id.name === functionName);
|
|
195
|
+
return declarations.length > 1;
|
|
196
|
+
}
|
|
197
|
+
container = container.parent;
|
|
72
198
|
}
|
|
73
199
|
return false;
|
|
74
200
|
}
|
|
75
201
|
function isInterfaceOrAbstractMethodSignature(node) {
|
|
202
|
+
if (node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition)
|
|
203
|
+
return true;
|
|
76
204
|
if (node.type === utils_1.AST_NODE_TYPES.TSMethodSignature)
|
|
77
205
|
return true;
|
|
78
206
|
if (node.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
@@ -133,7 +261,8 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
|
133
261
|
},
|
|
134
262
|
],
|
|
135
263
|
messages: {
|
|
136
|
-
|
|
264
|
+
noExplicitReturnTypeInferable: "What's wrong: {{functionKind}} has an explicit return type annotation. \u2192 Why it matters: it must be updated manually and can drift from what the implementation actually returns, hiding bugs behind a stale type. \u2192 How to fix: remove the return type annotation so TypeScript infers it from the implementation.",
|
|
265
|
+
noExplicitReturnTypeNonInferable: "What's wrong: {{functionKind}} has an explicit return type annotation but no implementation body. \u2192 Why it matters: TypeScript cannot infer the return type here; removing it widens the return type to `any`. \u2192 How to fix: keep the annotation, or provide an implementation body where inference can succeed.",
|
|
137
266
|
},
|
|
138
267
|
},
|
|
139
268
|
defaultOptions: [defaultOptions],
|
|
@@ -141,58 +270,77 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
|
141
270
|
const mergedOptions = { ...defaultOptions, ...options };
|
|
142
271
|
const filename = context.getFilename();
|
|
143
272
|
if ((mergedOptions.allowDtsFiles && filename.endsWith('.d.ts')) ||
|
|
144
|
-
(mergedOptions.allowFirestoreFunctionFiles &&
|
|
273
|
+
(mergedOptions.allowFirestoreFunctionFiles &&
|
|
274
|
+
filename.endsWith('.f.ts'))) {
|
|
145
275
|
return {};
|
|
146
276
|
}
|
|
147
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
277
|
function fixReturnType(fixer, node) {
|
|
149
|
-
|
|
278
|
+
// Some nodes expose returnType directly while others nest it under value.
|
|
279
|
+
const returnType = 'returnType' in node
|
|
280
|
+
? node.returnType
|
|
281
|
+
: 'value' in node
|
|
282
|
+
? node.value.returnType
|
|
283
|
+
: null;
|
|
150
284
|
if (!returnType)
|
|
151
285
|
return null;
|
|
152
|
-
// Create a fix that removes the return type annotation
|
|
153
286
|
return fixer.remove(returnType);
|
|
154
287
|
}
|
|
155
288
|
return {
|
|
156
289
|
FunctionDeclaration(node) {
|
|
157
|
-
|
|
290
|
+
const returnType = node.returnType;
|
|
291
|
+
if (!returnType)
|
|
158
292
|
return;
|
|
159
293
|
if (isTypeGuardFunction(node) ||
|
|
160
294
|
(mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node))) {
|
|
161
295
|
return;
|
|
162
296
|
}
|
|
297
|
+
const isInferable = Boolean(node.body);
|
|
163
298
|
context.report({
|
|
164
|
-
node:
|
|
165
|
-
messageId:
|
|
166
|
-
|
|
299
|
+
node: returnType,
|
|
300
|
+
messageId: isInferable
|
|
301
|
+
? 'noExplicitReturnTypeInferable'
|
|
302
|
+
: 'noExplicitReturnTypeNonInferable',
|
|
303
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
304
|
+
...(isInferable
|
|
305
|
+
? { fix: (fixer) => fixReturnType(fixer, node) }
|
|
306
|
+
: {}),
|
|
167
307
|
});
|
|
168
308
|
},
|
|
169
309
|
FunctionExpression(node) {
|
|
170
|
-
|
|
310
|
+
const returnType = node.returnType;
|
|
311
|
+
if (!returnType)
|
|
312
|
+
return;
|
|
313
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
|
|
171
314
|
return;
|
|
315
|
+
}
|
|
172
316
|
if (isTypeGuardFunction(node) ||
|
|
173
317
|
(mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node))) {
|
|
174
318
|
return;
|
|
175
319
|
}
|
|
176
320
|
context.report({
|
|
177
|
-
node:
|
|
178
|
-
messageId: '
|
|
321
|
+
node: returnType,
|
|
322
|
+
messageId: 'noExplicitReturnTypeInferable',
|
|
323
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
179
324
|
fix: (fixer) => fixReturnType(fixer, node),
|
|
180
325
|
});
|
|
181
326
|
},
|
|
182
327
|
ArrowFunctionExpression(node) {
|
|
183
|
-
|
|
328
|
+
const returnType = node.returnType;
|
|
329
|
+
if (!returnType)
|
|
184
330
|
return;
|
|
185
331
|
if (isTypeGuardFunction(node)) {
|
|
186
332
|
return;
|
|
187
333
|
}
|
|
188
334
|
context.report({
|
|
189
|
-
node:
|
|
190
|
-
messageId: '
|
|
335
|
+
node: returnType,
|
|
336
|
+
messageId: 'noExplicitReturnTypeInferable',
|
|
337
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
191
338
|
fix: (fixer) => fixReturnType(fixer, node),
|
|
192
339
|
});
|
|
193
340
|
},
|
|
194
341
|
TSMethodSignature(node) {
|
|
195
|
-
|
|
342
|
+
const returnType = node.returnType;
|
|
343
|
+
if (!returnType)
|
|
196
344
|
return;
|
|
197
345
|
if (mergedOptions.allowInterfaceMethodSignatures) {
|
|
198
346
|
return;
|
|
@@ -202,23 +350,64 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
|
|
|
202
350
|
return;
|
|
203
351
|
}
|
|
204
352
|
context.report({
|
|
205
|
-
node:
|
|
206
|
-
messageId: '
|
|
207
|
-
|
|
353
|
+
node: returnType,
|
|
354
|
+
messageId: 'noExplicitReturnTypeNonInferable',
|
|
355
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
208
356
|
});
|
|
209
357
|
},
|
|
210
358
|
MethodDefinition(node) {
|
|
211
|
-
|
|
359
|
+
const returnType = node.value.returnType;
|
|
360
|
+
if (!returnType)
|
|
212
361
|
return;
|
|
213
362
|
if (isTypeGuardFunction(node.value) ||
|
|
214
363
|
(mergedOptions.allowAbstractMethodSignatures &&
|
|
215
364
|
isInterfaceOrAbstractMethodSignature(node))) {
|
|
216
365
|
return;
|
|
217
366
|
}
|
|
367
|
+
const isInferable = Boolean(node.value.body);
|
|
218
368
|
context.report({
|
|
219
|
-
node:
|
|
220
|
-
messageId:
|
|
221
|
-
|
|
369
|
+
node: returnType,
|
|
370
|
+
messageId: isInferable
|
|
371
|
+
? 'noExplicitReturnTypeInferable'
|
|
372
|
+
: 'noExplicitReturnTypeNonInferable',
|
|
373
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
374
|
+
...(isInferable
|
|
375
|
+
? { fix: (fixer) => fixReturnType(fixer, node) }
|
|
376
|
+
: {}),
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
TSAbstractMethodDefinition(node) {
|
|
380
|
+
const returnType = node.value.returnType;
|
|
381
|
+
if (!returnType)
|
|
382
|
+
return;
|
|
383
|
+
if (isTypeGuardFunction(node.value) ||
|
|
384
|
+
(mergedOptions.allowAbstractMethodSignatures &&
|
|
385
|
+
isInterfaceOrAbstractMethodSignature(node))) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// Abstract methods never have bodies; they are always non-inferable and
|
|
389
|
+
// intentionally have no fixer.
|
|
390
|
+
context.report({
|
|
391
|
+
node: returnType,
|
|
392
|
+
messageId: 'noExplicitReturnTypeNonInferable',
|
|
393
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
TSDeclareFunction(node) {
|
|
397
|
+
const returnType = node.returnType;
|
|
398
|
+
if (!returnType)
|
|
399
|
+
return;
|
|
400
|
+
if (isTypeGuardFunction(node)) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (mergedOptions.allowOverloadedFunctions &&
|
|
404
|
+
isOverloadedTsDeclareFunction(node)) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
context.report({
|
|
408
|
+
node: returnType,
|
|
409
|
+
messageId: 'noExplicitReturnTypeNonInferable',
|
|
410
|
+
data: { functionKind: describeFunctionKind(node) },
|
|
222
411
|
});
|
|
223
412
|
},
|
|
224
413
|
};
|
|
@@ -15,9 +15,13 @@ exports.noFilterWithoutReturn = (0, createRule_1.createRule)({
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
if (!ASTHelpers_1.ASTHelpers.hasReturnStatement(body)) {
|
|
18
|
+
const filterCall = context.getSourceCode().getText(node.callee);
|
|
18
19
|
context.report({
|
|
19
20
|
node,
|
|
20
21
|
messageId: 'unexpected',
|
|
22
|
+
data: {
|
|
23
|
+
filterCall,
|
|
24
|
+
},
|
|
21
25
|
});
|
|
22
26
|
}
|
|
23
27
|
}
|
|
@@ -33,7 +37,7 @@ exports.noFilterWithoutReturn = (0, createRule_1.createRule)({
|
|
|
33
37
|
},
|
|
34
38
|
schema: [],
|
|
35
39
|
messages: {
|
|
36
|
-
unexpected: '
|
|
40
|
+
unexpected: 'Callback for {{filterCall}} uses braces but never returns a value, so filter receives undefined for every element and silently drops them all. Return the predicate result from inside the block (e.g., "return matches(item);") or use a concise arrow like {{filterCall}}((item) => matches(item)) to make the keep/remove condition explicit.',
|
|
37
41
|
},
|
|
38
42
|
},
|
|
39
43
|
defaultOptions: [],
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
export declare const noFirestoreJestMock: TSESLint.RuleModule<"noFirestoreJestMock", [], TSESLint.RuleListener>;
|