@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,576 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noEmptyDependencyUseCallbacks = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const minimatch_1 = require("minimatch");
|
|
6
|
+
const ASTHelpers_1 = require("../utils/ASTHelpers");
|
|
7
|
+
const createRule_1 = require("../utils/createRule");
|
|
8
|
+
const DEFAULT_TEST_PATTERNS = ['**/__tests__/**', '**/*.test.*', '**/*.spec.*'];
|
|
9
|
+
const PREFER_UTILITY_FUNCTION_MESSAGE = [
|
|
10
|
+
'What\'s wrong: "{{name}}" uses useCallback([]) but never reads component/hook state',
|
|
11
|
+
'Why it matters: empty-deps callbacks are already stable, so hook bookkeeping adds overhead without benefit',
|
|
12
|
+
'How to fix: move "{{name}}" to a module-level utility (or add an eslint-disable with a short justification if you intentionally keep it for memoized children).',
|
|
13
|
+
].join(' -> ');
|
|
14
|
+
const PREFER_UTILITY_LATEST_MESSAGE = [
|
|
15
|
+
'What\'s wrong: useLatestCallback wraps "{{name}}" even though it never reads component/hook state',
|
|
16
|
+
'Why it matters: the hook wrapper adds indirection without preventing stale closures',
|
|
17
|
+
'How to fix: extract "{{name}}" to a module-level utility (or disable with a brief note if you rely on HMR or a stale-closure pattern).',
|
|
18
|
+
].join(' -> ');
|
|
19
|
+
function isHookCallee(callee, hookName) {
|
|
20
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
21
|
+
return callee.name === hookName;
|
|
22
|
+
}
|
|
23
|
+
return (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
24
|
+
!callee.computed &&
|
|
25
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
26
|
+
callee.property.name === hookName);
|
|
27
|
+
}
|
|
28
|
+
function isUseCallbackCallee(callee) {
|
|
29
|
+
return isHookCallee(callee, 'useCallback');
|
|
30
|
+
}
|
|
31
|
+
function isUseLatestCallbackCallee(callee) {
|
|
32
|
+
return isHookCallee(callee, 'useLatestCallback');
|
|
33
|
+
}
|
|
34
|
+
function isFunctionExpression(node) {
|
|
35
|
+
return (!!node &&
|
|
36
|
+
(node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
37
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression));
|
|
38
|
+
}
|
|
39
|
+
function isPropertyKey(parent, identifier) {
|
|
40
|
+
if (!parent)
|
|
41
|
+
return false;
|
|
42
|
+
if (parent.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
43
|
+
parent.property === identifier &&
|
|
44
|
+
!parent.computed) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (parent.type === utils_1.AST_NODE_TYPES.Property &&
|
|
48
|
+
parent.key === identifier &&
|
|
49
|
+
!parent.computed &&
|
|
50
|
+
!parent.shorthand) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function collectNearestBlockTypeBindings(node) {
|
|
56
|
+
const localTypes = new Set();
|
|
57
|
+
let current = node.parent ?? undefined;
|
|
58
|
+
const addTypeParameters = (maybeNode) => {
|
|
59
|
+
if (!maybeNode)
|
|
60
|
+
return;
|
|
61
|
+
const typeParameters = maybeNode.typeParameters;
|
|
62
|
+
if (typeParameters &&
|
|
63
|
+
typeParameters.type === utils_1.AST_NODE_TYPES.TSTypeParameterDeclaration) {
|
|
64
|
+
for (const param of typeParameters.params) {
|
|
65
|
+
const name = typeof param.name === 'string' ? param.name : param.name.name;
|
|
66
|
+
localTypes.add(name);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
while (current) {
|
|
71
|
+
addTypeParameters(current);
|
|
72
|
+
if (current.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
73
|
+
for (const statement of current.body) {
|
|
74
|
+
if (statement.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration ||
|
|
75
|
+
statement.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration ||
|
|
76
|
+
statement.type === utils_1.AST_NODE_TYPES.ClassDeclaration ||
|
|
77
|
+
statement.type === utils_1.AST_NODE_TYPES.TSEnumDeclaration) {
|
|
78
|
+
localTypes.add(statement.id.name);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (statement.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration &&
|
|
82
|
+
(statement.id.type === utils_1.AST_NODE_TYPES.Identifier ||
|
|
83
|
+
statement.id.type === utils_1.AST_NODE_TYPES.Literal)) {
|
|
84
|
+
const name = statement.id.type === utils_1.AST_NODE_TYPES.Identifier
|
|
85
|
+
? statement.id.name
|
|
86
|
+
: String(statement.id.value);
|
|
87
|
+
localTypes.add(name);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (current.type === utils_1.AST_NODE_TYPES.Program) {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
current = current.parent ?? undefined;
|
|
95
|
+
}
|
|
96
|
+
return localTypes;
|
|
97
|
+
}
|
|
98
|
+
function usesLocalTypeBindings(typeRoots, localTypes, sourceCode) {
|
|
99
|
+
if (!typeRoots.length || localTypes.size === 0) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const visitorKeys = sourceCode.visitorKeys;
|
|
103
|
+
const stack = [...typeRoots];
|
|
104
|
+
while (stack.length) {
|
|
105
|
+
const current = stack.pop();
|
|
106
|
+
if (current.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
107
|
+
localTypes.has(current.name)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (current.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
|
|
111
|
+
let left = current.left;
|
|
112
|
+
while (left.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
|
|
113
|
+
left = left.left;
|
|
114
|
+
}
|
|
115
|
+
if (left.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
116
|
+
localTypes.has(left.name)) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
// Qualified names (e.g. External.LocalType) are scoped by their leftmost
|
|
120
|
+
// namespace. Traversing into the right side can falsely match an
|
|
121
|
+
// unrelated local type with the same name.
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const keys = visitorKeys[current.type];
|
|
125
|
+
if (!keys)
|
|
126
|
+
continue;
|
|
127
|
+
for (const key of keys) {
|
|
128
|
+
const value = current[key];
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
for (const element of value) {
|
|
131
|
+
if (element && typeof element.type === 'string') {
|
|
132
|
+
stack.push(element);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (value && typeof value.type === 'string') {
|
|
137
|
+
stack.push(value);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
function collectBodyTypeAnnotations(node, sourceCode) {
|
|
144
|
+
const typeNodes = [];
|
|
145
|
+
const visitorKeys = sourceCode.visitorKeys;
|
|
146
|
+
const stack = [node];
|
|
147
|
+
while (stack.length) {
|
|
148
|
+
const current = stack.pop();
|
|
149
|
+
if (current.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
|
|
150
|
+
current.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression) {
|
|
151
|
+
typeNodes.push(current.typeAnnotation);
|
|
152
|
+
}
|
|
153
|
+
else if (current.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
|
|
154
|
+
typeNodes.push(current.typeAnnotation);
|
|
155
|
+
}
|
|
156
|
+
const typeAnnotation = current
|
|
157
|
+
.typeAnnotation;
|
|
158
|
+
if (typeAnnotation) {
|
|
159
|
+
if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
160
|
+
typeNodes.push(typeAnnotation.typeAnnotation);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
typeNodes.push(typeAnnotation);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const typeParameters = current.typeParameters;
|
|
167
|
+
if (typeParameters) {
|
|
168
|
+
typeNodes.push(typeParameters);
|
|
169
|
+
}
|
|
170
|
+
const returnType = current.returnType;
|
|
171
|
+
if (returnType) {
|
|
172
|
+
typeNodes.push(returnType);
|
|
173
|
+
}
|
|
174
|
+
const keys = visitorKeys[current.type];
|
|
175
|
+
if (!keys)
|
|
176
|
+
continue;
|
|
177
|
+
for (const key of keys) {
|
|
178
|
+
const value = current[key];
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
for (const element of value) {
|
|
181
|
+
if (element && typeof element.type === 'string') {
|
|
182
|
+
stack.push(element);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else if (value && typeof value.type === 'string') {
|
|
187
|
+
stack.push(value);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return typeNodes;
|
|
192
|
+
}
|
|
193
|
+
function findHoistTarget(node) {
|
|
194
|
+
let current = node;
|
|
195
|
+
while (current?.parent) {
|
|
196
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.Program) {
|
|
197
|
+
return current;
|
|
198
|
+
}
|
|
199
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
200
|
+
current.parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
|
201
|
+
return current.parent;
|
|
202
|
+
}
|
|
203
|
+
current = current.parent;
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
function getCallbackArg(node) {
|
|
208
|
+
const [maybeFn] = node.arguments;
|
|
209
|
+
if (isFunctionExpression(maybeFn)) {
|
|
210
|
+
return maybeFn;
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
function hasEmptyDependencyArray(node) {
|
|
215
|
+
if (node.arguments.length < 2)
|
|
216
|
+
return false;
|
|
217
|
+
const deps = node.arguments[1];
|
|
218
|
+
return (deps.type === utils_1.AST_NODE_TYPES.ArrayExpression && deps.elements.length === 0);
|
|
219
|
+
}
|
|
220
|
+
function filenameMatchesPatterns(filename, patterns) {
|
|
221
|
+
const normalized = filename.replace(/\\/g, '/');
|
|
222
|
+
const basename = normalized.split('/').pop() ?? normalized;
|
|
223
|
+
return patterns.some((pattern) => (0, minimatch_1.minimatch)(normalized, pattern) || (0, minimatch_1.minimatch)(basename, pattern));
|
|
224
|
+
}
|
|
225
|
+
function usesThisOrSuper(node, sourceCode) {
|
|
226
|
+
const visitorKeys = sourceCode.visitorKeys;
|
|
227
|
+
const stack = [node];
|
|
228
|
+
while (stack.length) {
|
|
229
|
+
const current = stack.pop();
|
|
230
|
+
if (current.type === utils_1.AST_NODE_TYPES.ThisExpression ||
|
|
231
|
+
current.type === utils_1.AST_NODE_TYPES.Super) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
const keys = visitorKeys[current.type];
|
|
235
|
+
if (!keys)
|
|
236
|
+
continue;
|
|
237
|
+
for (const key of keys) {
|
|
238
|
+
const value = current[key];
|
|
239
|
+
if (Array.isArray(value)) {
|
|
240
|
+
for (const element of value) {
|
|
241
|
+
if (element && typeof element.type === 'string') {
|
|
242
|
+
stack.push(element);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (value && typeof value.type === 'string') {
|
|
247
|
+
stack.push(value);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
function analyzeExternalReferences(context, fn, extraTypeRoots = []) {
|
|
254
|
+
const sourceCode = context.getSourceCode();
|
|
255
|
+
if (usesThisOrSuper(fn.body, sourceCode)) {
|
|
256
|
+
return { hasComponentScopeRef: true };
|
|
257
|
+
}
|
|
258
|
+
let hasComponentScopeRef = false;
|
|
259
|
+
const scopeManager = sourceCode.scopeManager;
|
|
260
|
+
if (!scopeManager) {
|
|
261
|
+
return { hasComponentScopeRef: true };
|
|
262
|
+
}
|
|
263
|
+
const scope = scopeManager.acquire(fn, true) ?? scopeManager.acquire(fn);
|
|
264
|
+
if (!scope) {
|
|
265
|
+
return { hasComponentScopeRef: true };
|
|
266
|
+
}
|
|
267
|
+
if (!hasComponentScopeRef) {
|
|
268
|
+
for (const ref of scope.through) {
|
|
269
|
+
const identifier = ref.identifier;
|
|
270
|
+
if (isPropertyKey(identifier.parent, identifier)) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const resolved = ref.resolved;
|
|
274
|
+
if (!resolved) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const def = resolved.defs[0];
|
|
278
|
+
const scopeType = resolved.scope.type;
|
|
279
|
+
const isImport = def?.type === 'ImportBinding';
|
|
280
|
+
const isModuleOrGlobal = scopeType === 'module' || scopeType === 'global';
|
|
281
|
+
if (!isImport && !isModuleOrGlobal) {
|
|
282
|
+
hasComponentScopeRef = true;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (!hasComponentScopeRef) {
|
|
288
|
+
const typeRoots = [];
|
|
289
|
+
if (fn.returnType) {
|
|
290
|
+
typeRoots.push(fn.returnType.typeAnnotation);
|
|
291
|
+
}
|
|
292
|
+
if (fn.typeParameters) {
|
|
293
|
+
typeRoots.push(fn.typeParameters);
|
|
294
|
+
}
|
|
295
|
+
for (const param of fn.params) {
|
|
296
|
+
if ('typeAnnotation' in param && param.typeAnnotation) {
|
|
297
|
+
typeRoots.push(param.typeAnnotation.typeAnnotation);
|
|
298
|
+
}
|
|
299
|
+
else if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
|
|
300
|
+
'typeAnnotation' in param.left &&
|
|
301
|
+
param.left.typeAnnotation) {
|
|
302
|
+
typeRoots.push(param.left.typeAnnotation.typeAnnotation);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
typeRoots.push(...collectBodyTypeAnnotations(fn.body, sourceCode));
|
|
306
|
+
typeRoots.push(...extraTypeRoots);
|
|
307
|
+
if (typeRoots.some((typeRoot) => usesThisOrSuper(typeRoot, sourceCode))) {
|
|
308
|
+
hasComponentScopeRef = true;
|
|
309
|
+
}
|
|
310
|
+
if (!hasComponentScopeRef) {
|
|
311
|
+
const localTypes = collectNearestBlockTypeBindings(fn);
|
|
312
|
+
if (localTypes.size > 0 &&
|
|
313
|
+
usesLocalTypeBindings(typeRoots, localTypes, sourceCode)) {
|
|
314
|
+
hasComponentScopeRef = true;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return { hasComponentScopeRef };
|
|
319
|
+
}
|
|
320
|
+
function getProgramNode(node) {
|
|
321
|
+
let current = node;
|
|
322
|
+
while (current && current.type !== utils_1.AST_NODE_TYPES.Program) {
|
|
323
|
+
current = current.parent ?? undefined;
|
|
324
|
+
}
|
|
325
|
+
return current?.type === utils_1.AST_NODE_TYPES.Program ? current : null;
|
|
326
|
+
}
|
|
327
|
+
function addPatternIdentifiersToSet(pattern, names) {
|
|
328
|
+
switch (pattern.type) {
|
|
329
|
+
case utils_1.AST_NODE_TYPES.Identifier:
|
|
330
|
+
names.add(pattern.name);
|
|
331
|
+
return;
|
|
332
|
+
case utils_1.AST_NODE_TYPES.ObjectPattern:
|
|
333
|
+
for (const property of pattern.properties) {
|
|
334
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property) {
|
|
335
|
+
addPatternIdentifiersToSet(property.value, names);
|
|
336
|
+
}
|
|
337
|
+
else if (property.type === utils_1.AST_NODE_TYPES.RestElement) {
|
|
338
|
+
addPatternIdentifiersToSet(property.argument, names);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
case utils_1.AST_NODE_TYPES.ArrayPattern:
|
|
343
|
+
for (const element of pattern.elements) {
|
|
344
|
+
if (!element)
|
|
345
|
+
continue;
|
|
346
|
+
if (element.type === utils_1.AST_NODE_TYPES.RestElement) {
|
|
347
|
+
addPatternIdentifiersToSet(element.argument, names);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
addPatternIdentifiersToSet(element, names);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
354
|
+
case utils_1.AST_NODE_TYPES.RestElement:
|
|
355
|
+
addPatternIdentifiersToSet(pattern.argument, names);
|
|
356
|
+
return;
|
|
357
|
+
case utils_1.AST_NODE_TYPES.AssignmentPattern:
|
|
358
|
+
addPatternIdentifiersToSet(pattern.left, names);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function getModuleScopeValueBindings(program) {
|
|
363
|
+
const names = new Set();
|
|
364
|
+
for (const statement of program.body) {
|
|
365
|
+
const target = statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
366
|
+
statement.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration
|
|
367
|
+
? statement.declaration
|
|
368
|
+
: statement;
|
|
369
|
+
if (!target)
|
|
370
|
+
continue;
|
|
371
|
+
if (target.type === utils_1.AST_NODE_TYPES.ImportDeclaration) {
|
|
372
|
+
for (const specifier of target.specifiers) {
|
|
373
|
+
names.add(specifier.local.name);
|
|
374
|
+
}
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (target.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
378
|
+
for (const declaration of target.declarations) {
|
|
379
|
+
addPatternIdentifiersToSet(declaration.id, names);
|
|
380
|
+
}
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (target.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
384
|
+
target.type === utils_1.AST_NODE_TYPES.ClassDeclaration ||
|
|
385
|
+
target.type === utils_1.AST_NODE_TYPES.TSEnumDeclaration) {
|
|
386
|
+
if (target.id) {
|
|
387
|
+
names.add(target.id.name);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return names;
|
|
392
|
+
}
|
|
393
|
+
function buildHoistFixes(context, callExpression, callback, hoistedIdentifierCache) {
|
|
394
|
+
if (!callExpression.parent ||
|
|
395
|
+
callExpression.parent.type !== utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
const declarator = callExpression.parent;
|
|
399
|
+
if (declarator.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const varDecl = declarator.parent;
|
|
403
|
+
if (!varDecl ||
|
|
404
|
+
varDecl.type !== utils_1.AST_NODE_TYPES.VariableDeclaration ||
|
|
405
|
+
varDecl.declarations.length !== 1) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
if (varDecl.kind !== 'const') {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
if (!varDecl.parent ||
|
|
412
|
+
varDecl.parent.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
const hoistTarget = findHoistTarget(varDecl.parent);
|
|
416
|
+
if (!hoistTarget) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const programNode = getProgramNode(hoistTarget);
|
|
420
|
+
let moduleBindings = null;
|
|
421
|
+
if (programNode) {
|
|
422
|
+
const cachedBindings = hoistedIdentifierCache.get(programNode);
|
|
423
|
+
moduleBindings = cachedBindings ?? getModuleScopeValueBindings(programNode);
|
|
424
|
+
if (!cachedBindings) {
|
|
425
|
+
hoistedIdentifierCache.set(programNode, moduleBindings);
|
|
426
|
+
}
|
|
427
|
+
if (moduleBindings.has(declarator.id.name)) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
// Reserve the identifier so later callbacks in the same file with the same
|
|
431
|
+
// name do not attempt to hoist and produce duplicate declarations.
|
|
432
|
+
moduleBindings.add(declarator.id.name);
|
|
433
|
+
}
|
|
434
|
+
const sourceCode = context.getSourceCode();
|
|
435
|
+
// We use the full range of the id to ensure we capture any type annotations.
|
|
436
|
+
// In @typescript-eslint/parser, the Identifier range already includes the
|
|
437
|
+
// type annotation, but we explicitly calculate the end range for robustness.
|
|
438
|
+
const idRangeEnd = declarator.id.typeAnnotation &&
|
|
439
|
+
declarator.id.typeAnnotation.range[1] > declarator.id.range[1]
|
|
440
|
+
? declarator.id.typeAnnotation.range[1]
|
|
441
|
+
: declarator.id.range[1];
|
|
442
|
+
const identifierText = sourceCode
|
|
443
|
+
.getText()
|
|
444
|
+
.slice(declarator.id.range[0], idRangeEnd);
|
|
445
|
+
const functionText = sourceCode.getText(callback);
|
|
446
|
+
const hoisted = `const ${identifierText} = ${functionText};\n`;
|
|
447
|
+
const fileText = sourceCode.getText();
|
|
448
|
+
let removeStart = varDecl.range[0];
|
|
449
|
+
const lineStart = fileText.lastIndexOf('\n', removeStart - 1) + 1;
|
|
450
|
+
const leadingSegment = fileText.slice(lineStart, removeStart);
|
|
451
|
+
const hasOnlyIndentBefore = /^[ \t]*$/.test(leadingSegment);
|
|
452
|
+
if (hasOnlyIndentBefore) {
|
|
453
|
+
removeStart = lineStart;
|
|
454
|
+
}
|
|
455
|
+
let removeEnd = varDecl.range[1];
|
|
456
|
+
const lineEnd = fileText.indexOf('\n', removeEnd);
|
|
457
|
+
const segmentEnd = lineEnd === -1 ? fileText.length : lineEnd;
|
|
458
|
+
const trailingSegment = fileText.slice(removeEnd, segmentEnd);
|
|
459
|
+
const hasOnlyWhitespaceAfter = /^[ \t]*$/.test(trailingSegment);
|
|
460
|
+
if (hasOnlyWhitespaceAfter) {
|
|
461
|
+
removeEnd = segmentEnd;
|
|
462
|
+
}
|
|
463
|
+
if (hasOnlyIndentBefore && hasOnlyWhitespaceAfter && lineEnd !== -1) {
|
|
464
|
+
removeEnd = lineEnd + 1;
|
|
465
|
+
}
|
|
466
|
+
return (fixer) => [
|
|
467
|
+
fixer.insertTextBefore(hoistTarget, hoisted),
|
|
468
|
+
fixer.removeRange([removeStart, removeEnd]),
|
|
469
|
+
];
|
|
470
|
+
}
|
|
471
|
+
exports.noEmptyDependencyUseCallbacks = (0, createRule_1.createRule)({
|
|
472
|
+
name: 'no-empty-dependency-use-callbacks',
|
|
473
|
+
meta: {
|
|
474
|
+
type: 'suggestion',
|
|
475
|
+
docs: {
|
|
476
|
+
description: 'Discourage useCallback([]) or useLatestCallback around static functions. Static callbacks do not need hook machinery—extract them to module-level utilities for clarity and to avoid unnecessary hook overhead.',
|
|
477
|
+
recommended: 'error',
|
|
478
|
+
},
|
|
479
|
+
fixable: 'code',
|
|
480
|
+
schema: [
|
|
481
|
+
{
|
|
482
|
+
type: 'object',
|
|
483
|
+
properties: {
|
|
484
|
+
ignoreTestFiles: { type: 'boolean', default: true },
|
|
485
|
+
testFilePatterns: {
|
|
486
|
+
type: 'array',
|
|
487
|
+
items: { type: 'string' },
|
|
488
|
+
default: DEFAULT_TEST_PATTERNS,
|
|
489
|
+
},
|
|
490
|
+
ignoreUseLatestCallback: { type: 'boolean', default: false },
|
|
491
|
+
},
|
|
492
|
+
additionalProperties: false,
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
messages: {
|
|
496
|
+
preferUtilityFunction: PREFER_UTILITY_FUNCTION_MESSAGE,
|
|
497
|
+
preferUtilityLatest: PREFER_UTILITY_LATEST_MESSAGE,
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
defaultOptions: [{}],
|
|
501
|
+
create(context) {
|
|
502
|
+
const [options] = context.options;
|
|
503
|
+
const ignoreTestFiles = options?.ignoreTestFiles !== false;
|
|
504
|
+
const testPatterns = options?.testFilePatterns ?? DEFAULT_TEST_PATTERNS;
|
|
505
|
+
const ignoreUseLatestCallback = options?.ignoreUseLatestCallback === true;
|
|
506
|
+
const hoistedIdentifierCache = new WeakMap();
|
|
507
|
+
const filename = context.getFilename();
|
|
508
|
+
const isTest = ignoreTestFiles && filenameMatchesPatterns(filename, testPatterns);
|
|
509
|
+
function reportIfStaticCallback(callExpression, messageId) {
|
|
510
|
+
const callee = callExpression.callee;
|
|
511
|
+
if (callee.type !== utils_1.AST_NODE_TYPES.Identifier &&
|
|
512
|
+
callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const callback = getCallbackArg(callExpression);
|
|
516
|
+
if (!callback)
|
|
517
|
+
return;
|
|
518
|
+
if (ASTHelpers_1.ASTHelpers.returnsJSX(callback.body))
|
|
519
|
+
return;
|
|
520
|
+
const extraTypeRoots = [];
|
|
521
|
+
if (callExpression.parent &&
|
|
522
|
+
callExpression.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
523
|
+
'typeAnnotation' in callExpression.parent.id &&
|
|
524
|
+
callExpression.parent.id.typeAnnotation) {
|
|
525
|
+
const typeAnnotation = callExpression.parent.id.typeAnnotation;
|
|
526
|
+
if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
|
|
527
|
+
extraTypeRoots.push(typeAnnotation.typeAnnotation);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
extraTypeRoots.push(typeAnnotation);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const { hasComponentScopeRef } = analyzeExternalReferences(context, callback, extraTypeRoots);
|
|
534
|
+
if (hasComponentScopeRef) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const callbackName = callExpression.parent &&
|
|
538
|
+
callExpression.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
539
|
+
callExpression.parent.id.type === utils_1.AST_NODE_TYPES.Identifier
|
|
540
|
+
? callExpression.parent.id.name
|
|
541
|
+
: 'this callback';
|
|
542
|
+
const fix = buildHoistFixes(context, callExpression, callback, hoistedIdentifierCache);
|
|
543
|
+
context.report({
|
|
544
|
+
node: callExpression,
|
|
545
|
+
messageId,
|
|
546
|
+
data: { name: callbackName },
|
|
547
|
+
fix,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
CallExpression(node) {
|
|
552
|
+
if (isTest)
|
|
553
|
+
return;
|
|
554
|
+
const { callee } = node;
|
|
555
|
+
if (callee.type !== utils_1.AST_NODE_TYPES.Identifier &&
|
|
556
|
+
callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (isUseCallbackCallee(callee)) {
|
|
560
|
+
if (!hasEmptyDependencyArray(node)) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
reportIfStaticCallback(node, 'preferUtilityFunction');
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (ignoreUseLatestCallback)
|
|
567
|
+
return;
|
|
568
|
+
if (isUseLatestCallbackCallee(callee)) {
|
|
569
|
+
reportIfStaticCallback(node, 'preferUtilityLatest');
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
},
|
|
574
|
+
});
|
|
575
|
+
exports.default = exports.noEmptyDependencyUseCallbacks;
|
|
576
|
+
//# sourceMappingURL=no-empty-dependency-use-callbacks.js.map
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
type MessageIds = 'avoidEntireObject' | 'removeUnusedDependency';
|
|
2
|
+
export declare const noEntireObjectHookDeps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
+
export {};
|