@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
|
@@ -2,58 +2,54 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noUndefinedNullPassthrough = void 0;
|
|
4
4
|
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
6
|
exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
|
|
6
7
|
create(context) {
|
|
7
8
|
return {
|
|
8
9
|
FunctionDeclaration(node) {
|
|
9
|
-
|
|
10
|
-
if (node.params.length !== 1) {
|
|
10
|
+
if (shouldSkipFunction(node)) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
//
|
|
14
|
-
if (node.
|
|
15
|
-
node.
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
// For functions with exactly one parameter, check if it's passing through null/undefined
|
|
14
|
+
if (node.params.length === 1) {
|
|
15
|
+
checkFunctionBody(node.body, node.params[0], context);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// For functions with multiple parameters, check for early returns without arguments
|
|
19
|
+
checkFunctionBodyForEarlyReturns(node.body, node.params, context);
|
|
18
20
|
}
|
|
19
|
-
checkFunctionBody(node.body, node.params[0], context);
|
|
20
21
|
},
|
|
21
22
|
ArrowFunctionExpression(node) {
|
|
22
|
-
|
|
23
|
-
if (node.params.length !== 1) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
// Skip if the function is part of a variable declaration that starts with 'use' (React hook)
|
|
27
|
-
const parent = node.parent;
|
|
28
|
-
if (parent &&
|
|
29
|
-
parent.type === 'VariableDeclarator' &&
|
|
30
|
-
parent.id.type === 'Identifier' &&
|
|
31
|
-
parent.id.name.startsWith('use')) {
|
|
23
|
+
if (shouldSkipFunction(node)) {
|
|
32
24
|
return;
|
|
33
25
|
}
|
|
34
26
|
// For arrow functions with block body
|
|
35
27
|
if (node.body.type === 'BlockStatement') {
|
|
36
|
-
|
|
28
|
+
if (node.params.length === 1) {
|
|
29
|
+
checkFunctionBody(node.body, node.params[0], context);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// For functions with multiple parameters, check for early returns without arguments
|
|
33
|
+
checkFunctionBodyForEarlyReturns(node.body, node.params, context);
|
|
34
|
+
}
|
|
37
35
|
}
|
|
38
|
-
else {
|
|
39
|
-
// For arrow functions with expression body (implicit return)
|
|
36
|
+
else if (node.params.length === 1) {
|
|
37
|
+
// For arrow functions with expression body (implicit return) and one parameter
|
|
40
38
|
checkImplicitReturn(node, context);
|
|
41
39
|
}
|
|
42
40
|
},
|
|
43
41
|
FunctionExpression(node) {
|
|
44
|
-
|
|
45
|
-
if (node.params.length !== 1) {
|
|
42
|
+
if (shouldSkipFunction(node)) {
|
|
46
43
|
return;
|
|
47
44
|
}
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
// For functions with exactly one parameter, check if it's passing through null/undefined
|
|
46
|
+
if (node.params.length === 1) {
|
|
47
|
+
checkFunctionBody(node.body, node.params[0], context);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// For functions with multiple parameters, check for early returns without arguments
|
|
51
|
+
checkFunctionBodyForEarlyReturns(node.body, node.params, context);
|
|
55
52
|
}
|
|
56
|
-
checkFunctionBody(node.body, node.params[0], context);
|
|
57
53
|
},
|
|
58
54
|
};
|
|
59
55
|
},
|
|
@@ -66,7 +62,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
|
|
|
66
62
|
},
|
|
67
63
|
schema: [],
|
|
68
64
|
messages: {
|
|
69
|
-
unexpected: '
|
|
65
|
+
unexpected: 'Function returns null or undefined when "{{paramName}}" is nullish, which just passes the missing value to callers. This hides where validation belongs, delays failures until runtime as null values propagate unpredictably, and forces every caller to add defensive branching—making bugs and debugging more likely. Validate the argument before calling (throw an error if it is required) or return a concrete fallback (for example, an empty array or default object) so this function always provides a meaningful result.',
|
|
70
66
|
},
|
|
71
67
|
},
|
|
72
68
|
defaultOptions: [],
|
|
@@ -106,6 +102,7 @@ function checkFunctionBody(body, param, context) {
|
|
|
106
102
|
context.report({
|
|
107
103
|
node: statement,
|
|
108
104
|
messageId: 'unexpected',
|
|
105
|
+
data: { paramName },
|
|
109
106
|
});
|
|
110
107
|
}
|
|
111
108
|
return;
|
|
@@ -122,6 +119,7 @@ function checkFunctionBody(body, param, context) {
|
|
|
122
119
|
context.report({
|
|
123
120
|
node: statement,
|
|
124
121
|
messageId: 'unexpected',
|
|
122
|
+
data: { paramName },
|
|
125
123
|
});
|
|
126
124
|
}
|
|
127
125
|
return;
|
|
@@ -136,18 +134,81 @@ function checkFunctionBody(body, param, context) {
|
|
|
136
134
|
function checkForTransformation(body, paramName) {
|
|
137
135
|
if (!paramName)
|
|
138
136
|
return false;
|
|
139
|
-
// Look for return statements
|
|
137
|
+
// Look for return statements or other statements that use the parameter in a transformation
|
|
140
138
|
for (const statement of body.body) {
|
|
141
139
|
if (statement.type === 'ReturnStatement' && statement.argument) {
|
|
142
|
-
// Check for
|
|
143
|
-
if (statement.argument
|
|
144
|
-
|
|
140
|
+
// Check for various transformation patterns
|
|
141
|
+
if (containsParameterTransformation(statement.argument, paramName)) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Check for other statements that might contain transformations (like for loops with yield)
|
|
146
|
+
if (statement.type === 'ForStatement' ||
|
|
147
|
+
statement.type === 'ForInStatement' ||
|
|
148
|
+
statement.type === 'ForOfStatement') {
|
|
149
|
+
if (containsTransformationInStatement(statement, paramName)) {
|
|
145
150
|
return true;
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
return false;
|
|
150
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if a statement contains a transformation of the parameter
|
|
158
|
+
*/
|
|
159
|
+
function containsTransformationInStatement(statement,
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
161
|
+
_paramName) {
|
|
162
|
+
// For generator functions, check if there are yield expressions with external function calls
|
|
163
|
+
if (statement.type === 'ForOfStatement' &&
|
|
164
|
+
statement.body.type === 'BlockStatement') {
|
|
165
|
+
for (const stmt of statement.body.body) {
|
|
166
|
+
if (stmt.type === 'ExpressionStatement' &&
|
|
167
|
+
stmt.expression.type === 'YieldExpression' &&
|
|
168
|
+
stmt.expression.argument &&
|
|
169
|
+
stmt.expression.argument.type === 'CallExpression' &&
|
|
170
|
+
stmt.expression.argument.callee.type === 'Identifier') {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Check if an expression contains a transformation of the parameter
|
|
179
|
+
* A transformation is considered to be calling external functions with the parameter,
|
|
180
|
+
* not just calling methods on the parameter itself.
|
|
181
|
+
*/
|
|
182
|
+
function containsParameterTransformation(node, paramName) {
|
|
183
|
+
// Check for direct function calls with the parameter: transformData(data)
|
|
184
|
+
// This is considered a transformation because it's calling an external function
|
|
185
|
+
if (node.type === 'CallExpression' &&
|
|
186
|
+
node.callee.type === 'Identifier' && // External function call
|
|
187
|
+
node.arguments.some((arg) => arg.type === 'Identifier' && arg.name === paramName)) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
// Check for Object.* method calls: Object.keys(data), Object.values(data), Object.entries(data)
|
|
191
|
+
if (node.type === 'CallExpression' &&
|
|
192
|
+
node.callee.type === 'MemberExpression' &&
|
|
193
|
+
node.callee.object.type === 'Identifier' &&
|
|
194
|
+
node.callee.object.name === 'Object' &&
|
|
195
|
+
node.arguments.some((arg) => arg.type === 'Identifier' && arg.name === paramName)) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
// Check for chained operations that start with external functions: Object.entries(rounds).reduce(...).sort(...)
|
|
199
|
+
if (node.type === 'CallExpression' &&
|
|
200
|
+
node.callee.type === 'MemberExpression' &&
|
|
201
|
+
node.callee.object.type === 'CallExpression' &&
|
|
202
|
+
node.callee.object.callee.type === 'MemberExpression' &&
|
|
203
|
+
node.callee.object.callee.object.type === 'Identifier' &&
|
|
204
|
+
node.callee.object.callee.object.name === 'Object' &&
|
|
205
|
+
node.callee.object.arguments.some((arg) => arg.type === 'Identifier' && arg.name === paramName)) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
// Method calls on the parameter itself (like data.process(), items.filter())
|
|
209
|
+
// are NOT considered transformations for the purpose of this rule
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
151
212
|
/**
|
|
152
213
|
* Check arrow functions with expression bodies (implicit returns)
|
|
153
214
|
*/
|
|
@@ -171,6 +232,7 @@ function checkImplicitReturn(node, context) {
|
|
|
171
232
|
context.report({
|
|
172
233
|
node,
|
|
173
234
|
messageId: 'unexpected',
|
|
235
|
+
data: { paramName },
|
|
174
236
|
});
|
|
175
237
|
}
|
|
176
238
|
}
|
|
@@ -181,6 +243,7 @@ function checkImplicitReturn(node, context) {
|
|
|
181
243
|
context.report({
|
|
182
244
|
node,
|
|
183
245
|
messageId: 'unexpected',
|
|
246
|
+
data: { paramName },
|
|
184
247
|
});
|
|
185
248
|
}
|
|
186
249
|
}
|
|
@@ -189,6 +252,7 @@ function checkImplicitReturn(node, context) {
|
|
|
189
252
|
context.report({
|
|
190
253
|
node,
|
|
191
254
|
messageId: 'unexpected',
|
|
255
|
+
data: { paramName },
|
|
192
256
|
});
|
|
193
257
|
}
|
|
194
258
|
}
|
|
@@ -205,8 +269,10 @@ function isNullUndefinedCheck(node, paramName) {
|
|
|
205
269
|
}
|
|
206
270
|
// Check for param === null, param === undefined, etc.
|
|
207
271
|
if (node.type === 'BinaryExpression' &&
|
|
208
|
-
(node.operator === '===' ||
|
|
209
|
-
node.operator === '
|
|
272
|
+
(node.operator === '===' ||
|
|
273
|
+
node.operator === '==' ||
|
|
274
|
+
node.operator === '!==' ||
|
|
275
|
+
node.operator === '!=')) {
|
|
210
276
|
const left = node.left;
|
|
211
277
|
const right = node.right;
|
|
212
278
|
// param === null/undefined
|
|
@@ -224,8 +290,7 @@ function isNullUndefinedCheck(node, paramName) {
|
|
|
224
290
|
}
|
|
225
291
|
}
|
|
226
292
|
// Check for param === null || param === undefined
|
|
227
|
-
if (node.type === 'LogicalExpression' &&
|
|
228
|
-
node.operator === '||') {
|
|
293
|
+
if (node.type === 'LogicalExpression' && node.operator === '||') {
|
|
229
294
|
return (isNullUndefinedCheck(node.left, paramName) ||
|
|
230
295
|
isNullUndefinedCheck(node.right, paramName));
|
|
231
296
|
}
|
|
@@ -251,4 +316,116 @@ function isParameterReference(node, paramName) {
|
|
|
251
316
|
return false;
|
|
252
317
|
return node.type === 'Identifier' && node.name === paramName;
|
|
253
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Check function body for early returns without arguments (implicit undefined)
|
|
321
|
+
* based on parameter null/undefined checks
|
|
322
|
+
*/
|
|
323
|
+
function checkFunctionBodyForEarlyReturns(body, params, context) {
|
|
324
|
+
// Get parameter names
|
|
325
|
+
const paramNames = params
|
|
326
|
+
.map((param) => {
|
|
327
|
+
if (param.type === 'Identifier') {
|
|
328
|
+
return param.name;
|
|
329
|
+
}
|
|
330
|
+
else if (param.type === 'AssignmentPattern' &&
|
|
331
|
+
param.left.type === 'Identifier') {
|
|
332
|
+
return param.left.name;
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
})
|
|
336
|
+
.filter((name) => name !== null);
|
|
337
|
+
if (paramNames.length === 0)
|
|
338
|
+
return;
|
|
339
|
+
// Look for early returns without arguments based on parameter checks
|
|
340
|
+
for (const statement of body.body) {
|
|
341
|
+
if (statement.type === 'IfStatement') {
|
|
342
|
+
const test = statement.test;
|
|
343
|
+
// Check if the test is checking any parameter for null/undefined
|
|
344
|
+
const matchedParam = paramNames.find((paramName) => isNullUndefinedCheck(test, paramName));
|
|
345
|
+
if (matchedParam) {
|
|
346
|
+
// Check if the consequent is a block statement with a return
|
|
347
|
+
if (statement.consequent.type === 'BlockStatement') {
|
|
348
|
+
for (const consequentStmt of statement.consequent.body) {
|
|
349
|
+
if (consequentStmt.type === 'ReturnStatement' &&
|
|
350
|
+
(!consequentStmt.argument ||
|
|
351
|
+
isNullOrUndefinedLiteral(consequentStmt.argument))) {
|
|
352
|
+
// Check if there's a transformation in the function
|
|
353
|
+
const hasTransformation = paramNames.some((paramName) => checkForTransformation(body, paramName));
|
|
354
|
+
if (!hasTransformation) {
|
|
355
|
+
context.report({
|
|
356
|
+
node: statement,
|
|
357
|
+
messageId: 'unexpected',
|
|
358
|
+
data: { paramName: matchedParam },
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Check if the consequent is a direct return statement without an argument
|
|
366
|
+
else if (statement.consequent.type === 'ReturnStatement' &&
|
|
367
|
+
(!statement.consequent.argument ||
|
|
368
|
+
isNullOrUndefinedLiteral(statement.consequent.argument))) {
|
|
369
|
+
// Check if there's a transformation in the function
|
|
370
|
+
const hasTransformation = paramNames.some((paramName) => checkForTransformation(body, paramName));
|
|
371
|
+
if (!hasTransformation) {
|
|
372
|
+
context.report({
|
|
373
|
+
node: statement,
|
|
374
|
+
messageId: 'unexpected',
|
|
375
|
+
data: { paramName: matchedParam },
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function shouldSkipFunction(node) {
|
|
385
|
+
// Skip functions with no parameters
|
|
386
|
+
if (node.params.length === 0) {
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
// Skip functions with any parameter typed as 'unknown'
|
|
390
|
+
if (node.params.some(isUnknownParameter)) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
// Skip React hooks (functions starting with 'use')
|
|
394
|
+
let name;
|
|
395
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
|
396
|
+
name = node.id?.name;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
const parent = node.parent;
|
|
400
|
+
if (parent &&
|
|
401
|
+
parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
402
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
403
|
+
name = parent.id.name;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (name?.startsWith('use')) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Exempts type-narrowing functions from the rule by checking for 'unknown' parameters.
|
|
413
|
+
* These functions often narrow from 'unknown' and return null/undefined for invalid types,
|
|
414
|
+
* which is a valid pattern.
|
|
415
|
+
*
|
|
416
|
+
* TODO: Handle edge cases like rest parameters (...args: unknown[]),
|
|
417
|
+
* destructured parameters ({ x }: unknown), and array patterns ([a]: unknown).
|
|
418
|
+
*/
|
|
419
|
+
function isUnknownParameter(param) {
|
|
420
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
421
|
+
return (param.typeAnnotation?.typeAnnotation.type ===
|
|
422
|
+
utils_1.AST_NODE_TYPES.TSUnknownKeyword);
|
|
423
|
+
}
|
|
424
|
+
if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
|
|
425
|
+
param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
426
|
+
return (param.left.typeAnnotation?.typeAnnotation.type ===
|
|
427
|
+
utils_1.AST_NODE_TYPES.TSUnknownKeyword);
|
|
428
|
+
}
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
254
431
|
//# sourceMappingURL=no-undefined-null-passthrough.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
ignoreHooks?: string[];
|
|
4
|
+
ignoreHocs?: string[];
|
|
5
|
+
}
|
|
6
|
+
];
|
|
7
|
+
export declare const noUnmemoizedMemoWithoutProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unnecessaryMemoWithoutProps", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
8
|
+
export {};
|