@blumintinc/eslint-plugin-blumint 1.13.0 → 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 +170 -139
- package/lib/index.js +190 -1
- package/lib/rules/array-methods-this-context.js +1 -1
- 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.js +3 -4
- package/lib/rules/enforce-assert-safe-object-key.js +11 -11
- package/lib/rules/enforce-boolean-naming-prefixes.js +11 -23
- package/lib/rules/enforce-callback-memo.js +1 -1
- package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
- package/lib/rules/enforce-console-error.js +35 -7
- 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-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 +1 -1
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +100 -50
- package/lib/rules/enforce-firestore-doc-ref-generic.js +15 -3
- package/lib/rules/enforce-firestore-facade.js +4 -2
- package/lib/rules/enforce-firestore-set-merge.js +3 -4
- package/lib/rules/enforce-global-constants.js +1 -1
- package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
- package/lib/rules/enforce-memoize-async.js +2 -6
- package/lib/rules/enforce-memoize-getters.js +39 -15
- package/lib/rules/enforce-microdiff.js +1 -1
- package/lib/rules/enforce-object-literal-as-const.js +4 -4
- package/lib/rules/enforce-positive-naming.js +4 -0
- package/lib/rules/enforce-props-argument-name.js +2 -2
- 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 +1 -1
- 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.js +7 -3
- 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 +5 -4
- 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 +40 -13
- package/lib/rules/fast-deep-equal-over-microdiff.js +29 -39
- package/lib/rules/firestore-transaction-reads-before-writes.js +50 -4
- package/lib/rules/flatten-push-calls.d.ts +2 -0
- package/lib/rules/flatten-push-calls.js +428 -0
- 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/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.js +9 -3
- 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 +7 -3
- package/lib/rules/no-compositing-layer-props.js +2 -2
- 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.js +4 -6
- package/lib/rules/no-excessive-parent-chain.js +1 -1
- 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 +67 -12
- 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 +1 -1
- 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.js +6 -6
- package/lib/rules/no-memoize-on-static.js +9 -1
- package/lib/rules/no-misleading-boolean-prefixes.js +7 -3
- 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.js +29 -17
- package/lib/rules/no-passthrough-getters.js +38 -2
- 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.js +16 -3
- 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.js +11 -10
- package/lib/rules/no-separate-loading-state.js +7 -16
- package/lib/rules/no-stale-state-across-await.js +1 -1
- package/lib/rules/no-type-assertion-returns.js +43 -49
- package/lib/rules/no-undefined-null-passthrough.js +61 -31
- 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 -1
- package/lib/rules/no-unpinned-dependencies.js +36 -5
- package/lib/rules/no-unused-props.d.ts +2 -2
- package/lib/rules/no-unused-props.js +295 -91
- 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.js +6 -4
- package/lib/rules/parallelize-async-operations.js +24 -5
- package/lib/rules/prefer-batch-operations.d.ts +1 -3
- package/lib/rules/prefer-batch-operations.js +32 -5
- package/lib/rules/prefer-block-comments-for-declarations.js +9 -4
- 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-field-paths-in-transforms.js +4 -3
- package/lib/rules/prefer-fragment-component.js +1 -1
- 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.js +88 -13
- 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.js +60 -69
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +9 -4
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +2 -1
- package/lib/rules/prefer-nullish-coalescing-override.js +11 -5
- package/lib/rules/prefer-params-over-parent-id.js +220 -171
- package/lib/rules/prefer-settings-object.js +2 -2
- package/lib/rules/prefer-type-over-interface.js +7 -2
- package/lib/rules/prefer-url-tostring-over-tojson.js +6 -3
- package/lib/rules/prefer-use-deep-compare-memo.js +8 -5
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
- 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.js +2 -2
- 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 +1 -1
- package/lib/rules/require-memo.js +1 -1
- 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 +2 -3
- package/lib/rules/test-file-location-enforcement.js +1 -1
- package/lib/rules/use-latest-callback.js +6 -5
- 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 +24 -0
- package/lib/utils/getMethodName.d.ts +27 -0
- package/lib/utils/getMethodName.js +35 -0
- package/lib/utils/tsTypeClassifier.d.ts +30 -0
- package/lib/utils/tsTypeClassifier.js +149 -0
- package/package.json +3 -1
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.noUnusedProps = void 0;
|
|
4
4
|
const utils_1 = require("@typescript-eslint/utils");
|
|
5
5
|
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const ASTHelpers_1 = require("../utils/ASTHelpers");
|
|
7
|
+
const PAREN_TYPE = utils_1.AST_NODE_TYPES.TSParenthesizedType ??
|
|
8
|
+
'TSParenthesizedType';
|
|
6
9
|
exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
7
10
|
name: 'no-unused-props',
|
|
8
11
|
meta: {
|
|
@@ -13,7 +16,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
13
16
|
},
|
|
14
17
|
schema: [],
|
|
15
18
|
messages: {
|
|
16
|
-
unusedProp: 'Prop "{{propName}}" is
|
|
19
|
+
unusedProp: 'Prop "{{propName}}" is declared in the component Props type but never used inside the component body. Unused props make the component API misleading: callers keep passing values that are ignored and reviewers assume behavior that is not implemented. Remove "{{propName}}" from the Props type, consume it in the component, or forward it with a rest spread (e.g., `const MyComponent = ({ usedProp, ...rest }: Props) => <Child {...rest} />`).',
|
|
17
20
|
},
|
|
18
21
|
fixable: 'code',
|
|
19
22
|
},
|
|
@@ -22,8 +25,16 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
22
25
|
const filename = context.filename ??
|
|
23
26
|
context.getFilename();
|
|
24
27
|
const ruleSettings = (context.settings && context.settings['no-unused-props']) ?? {};
|
|
25
|
-
const
|
|
26
|
-
|
|
28
|
+
const normalizeExtension = (ext) => {
|
|
29
|
+
const trimmed = ext.trim().toLowerCase();
|
|
30
|
+
if (!trimmed) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
|
|
34
|
+
};
|
|
35
|
+
const reactLikeExtensions = Array.from(new Set((ruleSettings.reactLikeExtensions ?? ['.tsx'])
|
|
36
|
+
.map(normalizeExtension)
|
|
37
|
+
.filter((ext) => Boolean(ext))));
|
|
27
38
|
const fileExtension = filename.includes('.')
|
|
28
39
|
? filename.slice(filename.lastIndexOf('.')).toLowerCase()
|
|
29
40
|
: '';
|
|
@@ -43,14 +54,43 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
43
54
|
'InstanceType',
|
|
44
55
|
'ThisType',
|
|
45
56
|
]);
|
|
57
|
+
// Marks spread types whose members cannot be enumerated so unknown used props
|
|
58
|
+
// can still count as "using something" from the imported/spread type.
|
|
59
|
+
const UNKNOWN_SPREAD_PROP = '__unknown_spread_prop__';
|
|
60
|
+
const markUnknownSpreadType = (spreadTypeProps, spreadTypeName) => {
|
|
61
|
+
spreadTypeProps[spreadTypeName] ??= [];
|
|
62
|
+
if (!spreadTypeProps[spreadTypeName].includes(UNKNOWN_SPREAD_PROP)) {
|
|
63
|
+
spreadTypeProps[spreadTypeName].push(UNKNOWN_SPREAD_PROP);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const isTypeAliasDeclaration = (node) => node?.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration;
|
|
67
|
+
const findTypeAliasDeclaration = (typeName) => {
|
|
68
|
+
let scope = context.getScope();
|
|
69
|
+
while (scope) {
|
|
70
|
+
const variable = scope.set?.get(typeName) ??
|
|
71
|
+
scope.variables.find((v) => v.name === typeName);
|
|
72
|
+
if (variable) {
|
|
73
|
+
const typeAliasDef = variable.defs.find((def) => def.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration);
|
|
74
|
+
const typeAliasNode = typeAliasDef?.node;
|
|
75
|
+
if (isTypeAliasDeclaration(typeAliasNode)) {
|
|
76
|
+
return typeAliasNode;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
scope = scope.upper;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
};
|
|
46
83
|
const propsTypes = new Map();
|
|
47
|
-
|
|
48
|
-
const
|
|
84
|
+
const componentToReferencedSpreadTypeNames = new Map();
|
|
85
|
+
const spreadTypeToPropNames = new Map();
|
|
86
|
+
const processingTypeAliases = new Set();
|
|
49
87
|
const componentsToCheck = [];
|
|
50
88
|
let currentComponent = null;
|
|
51
89
|
const clearState = () => {
|
|
52
90
|
propsTypes.clear();
|
|
53
|
-
|
|
91
|
+
componentToReferencedSpreadTypeNames.clear();
|
|
92
|
+
spreadTypeToPropNames.clear();
|
|
93
|
+
processingTypeAliases.clear();
|
|
54
94
|
componentsToCheck.length = 0;
|
|
55
95
|
currentComponent = null;
|
|
56
96
|
};
|
|
@@ -66,19 +106,44 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
66
106
|
}
|
|
67
107
|
return false;
|
|
68
108
|
};
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
109
|
+
const isTSParenthesizedType = (node) => node.type === PAREN_TYPE &&
|
|
110
|
+
node.typeAnnotation != null;
|
|
111
|
+
const getStaticPropName = (key) => {
|
|
112
|
+
if (key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
113
|
+
return key.name;
|
|
114
|
+
}
|
|
115
|
+
if (key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
116
|
+
typeof key.value === 'string') {
|
|
117
|
+
return key.value;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
const isAnyPropFromSpreadTypeUsed = (spreadTypeName, used, knownProps) => {
|
|
122
|
+
const spreadTypeProps = spreadTypeToPropNames.get(spreadTypeName);
|
|
123
|
+
if (!spreadTypeProps || spreadTypeProps.size === 0) {
|
|
72
124
|
return false;
|
|
73
125
|
}
|
|
126
|
+
if (spreadTypeProps.has(UNKNOWN_SPREAD_PROP)) {
|
|
127
|
+
for (const usedProp of used) {
|
|
128
|
+
if (usedProp.startsWith('...')) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!knownProps.has(usedProp)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
74
136
|
for (const spreadProp of spreadTypeProps) {
|
|
137
|
+
if (spreadProp === UNKNOWN_SPREAD_PROP) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
75
140
|
if (used.has(spreadProp)) {
|
|
76
141
|
return true;
|
|
77
142
|
}
|
|
78
143
|
}
|
|
79
144
|
return false;
|
|
80
145
|
};
|
|
81
|
-
const shouldSkipSpreadType = (prop, hasRestSpread, used) => {
|
|
146
|
+
const shouldSkipSpreadType = (prop, hasRestSpread, used, knownProps) => {
|
|
82
147
|
if (hasRestSpread) {
|
|
83
148
|
return true;
|
|
84
149
|
}
|
|
@@ -86,16 +151,18 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
86
151
|
if (UTILITY_TYPES.has(spreadTypeName)) {
|
|
87
152
|
return true;
|
|
88
153
|
}
|
|
89
|
-
return isAnyPropFromSpreadTypeUsed(spreadTypeName, used);
|
|
154
|
+
return isAnyPropFromSpreadTypeUsed(spreadTypeName, used, knownProps);
|
|
90
155
|
};
|
|
91
156
|
const shouldSkipPropFromSpreadType = (prop, currentTypeName, used) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
157
|
+
const spreadTypes = componentToReferencedSpreadTypeNames.get(currentTypeName);
|
|
158
|
+
if (!spreadTypes)
|
|
159
|
+
return false;
|
|
160
|
+
for (const spreadType of spreadTypes) {
|
|
161
|
+
const spreadProps = spreadTypeToPropNames.get(spreadType);
|
|
162
|
+
if (!spreadProps || !spreadProps.has(prop)) {
|
|
96
163
|
continue;
|
|
97
164
|
}
|
|
98
|
-
for (const spreadProp of
|
|
165
|
+
for (const spreadProp of spreadProps) {
|
|
99
166
|
if (used.has(spreadProp)) {
|
|
100
167
|
return true;
|
|
101
168
|
}
|
|
@@ -109,6 +176,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
109
176
|
return;
|
|
110
177
|
}
|
|
111
178
|
const hasRestSpread = hasRestSpreadUsage(used, restUsed);
|
|
179
|
+
const knownProps = new Set(Object.keys(propsType));
|
|
112
180
|
Object.keys(propsType).forEach((prop) => {
|
|
113
181
|
if (!used.has(prop)) {
|
|
114
182
|
// For imported types (props that start with '...'), only report if there's no rest spread operator
|
|
@@ -123,7 +191,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
123
191
|
shouldReport = false;
|
|
124
192
|
}
|
|
125
193
|
else if (prop.startsWith('...')) {
|
|
126
|
-
shouldReport = !shouldSkipSpreadType(prop, hasRestSpread, used);
|
|
194
|
+
shouldReport = !shouldSkipSpreadType(prop, hasRestSpread, used, knownProps);
|
|
127
195
|
}
|
|
128
196
|
else {
|
|
129
197
|
shouldReport = !shouldSkipPropFromSpreadType(prop, typeName, used);
|
|
@@ -140,62 +208,206 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
140
208
|
};
|
|
141
209
|
return {
|
|
142
210
|
TSTypeAliasDeclaration(node) {
|
|
143
|
-
if (node.id.name.endsWith('Props')) {
|
|
211
|
+
if (!node.id.name.endsWith('Props')) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (processingTypeAliases.has(node.id.name)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
processingTypeAliases.add(node.id.name);
|
|
218
|
+
try {
|
|
144
219
|
const props = {};
|
|
145
220
|
// Track which properties come from which spread type
|
|
146
221
|
const spreadTypeProps = {};
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
222
|
+
const visitingTypeReferences = new Set();
|
|
223
|
+
const scope = context.getScope();
|
|
224
|
+
const withTypeVisitation = (visited, typeName, callback) => {
|
|
225
|
+
if (visited.has(typeName)) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
visited.add(typeName);
|
|
229
|
+
try {
|
|
230
|
+
return callback();
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
visited.delete(typeName);
|
|
150
234
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
235
|
+
};
|
|
236
|
+
const addBaseTypeProps = (typeNode, shouldInclude = () => true) => {
|
|
237
|
+
// Tracks type identifiers while descending to avoid infinite recursion on
|
|
238
|
+
// self-referential or mutually recursive type aliases; names are removed
|
|
239
|
+
// in the finally block so parallel branches still run.
|
|
240
|
+
const visitingTypeNames = new Set();
|
|
241
|
+
const visit = (node) => {
|
|
242
|
+
if (isTSParenthesizedType(node)) {
|
|
243
|
+
return visit(node.typeAnnotation);
|
|
156
244
|
}
|
|
157
|
-
|
|
245
|
+
switch (node.type) {
|
|
246
|
+
case utils_1.AST_NODE_TYPES.TSTypeLiteral: {
|
|
247
|
+
let added = false;
|
|
248
|
+
node.members.forEach((member) => {
|
|
249
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const propName = getStaticPropName(member.key);
|
|
253
|
+
if (propName && shouldInclude(propName)) {
|
|
254
|
+
props[propName] = member.key;
|
|
255
|
+
added = true;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
return added;
|
|
259
|
+
}
|
|
260
|
+
case utils_1.AST_NODE_TYPES.TSIntersectionType:
|
|
261
|
+
case utils_1.AST_NODE_TYPES.TSUnionType: {
|
|
262
|
+
let added = false;
|
|
263
|
+
node.types.forEach((child) => {
|
|
264
|
+
if (visit(child)) {
|
|
265
|
+
added = true;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
return added;
|
|
269
|
+
}
|
|
270
|
+
case utils_1.AST_NODE_TYPES.TSTypeReference: {
|
|
271
|
+
if (node.typeName.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
const typeIdentifier = node.typeName;
|
|
275
|
+
return (withTypeVisitation(visitingTypeNames, typeIdentifier.name, () => {
|
|
276
|
+
const typeAliasNode = findTypeAliasDeclaration(typeIdentifier.name);
|
|
277
|
+
return typeAliasNode
|
|
278
|
+
? visit(typeAliasNode.typeAnnotation)
|
|
279
|
+
: false;
|
|
280
|
+
}) ?? false);
|
|
281
|
+
}
|
|
282
|
+
default:
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
return visit(typeNode);
|
|
158
287
|
};
|
|
159
|
-
const handleOmitType = (baseType, omittedProps) => {
|
|
160
|
-
|
|
161
|
-
baseType.typeName.type
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const
|
|
288
|
+
const handleOmitType = (baseType, omittedProps, currentScope) => {
|
|
289
|
+
const baseTypeName = baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
290
|
+
baseType.typeName.type === utils_1.AST_NODE_TYPES.Identifier
|
|
291
|
+
? baseType.typeName.name
|
|
292
|
+
: null;
|
|
293
|
+
const sourceCode = context.getSourceCode();
|
|
294
|
+
const baseTypeRefText = baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference
|
|
295
|
+
? sourceCode.getText(baseType.typeName)
|
|
296
|
+
: null;
|
|
297
|
+
const spreadKey = baseTypeName ?? baseTypeRefText;
|
|
298
|
+
const fallbackSpreadKey = spreadKey ?? sourceCode.getText(baseType);
|
|
299
|
+
const recordSpreadMarker = () => {
|
|
300
|
+
const markerKey = fallbackSpreadKey;
|
|
301
|
+
if (!markerKey) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
props[`...${markerKey}`] =
|
|
305
|
+
baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference
|
|
306
|
+
? baseType.typeName
|
|
307
|
+
: baseType;
|
|
308
|
+
markUnknownSpreadType(spreadTypeProps, markerKey);
|
|
309
|
+
};
|
|
165
310
|
const omittedPropNames = new Set();
|
|
311
|
+
let canComputeOmittedProps = false;
|
|
166
312
|
if (omittedProps.type === utils_1.AST_NODE_TYPES.TSUnionType) {
|
|
167
|
-
omittedProps.types.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
313
|
+
canComputeOmittedProps = omittedProps.types.every((type) => type.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
314
|
+
type.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
315
|
+
typeof type.literal.value === 'string');
|
|
316
|
+
if (canComputeOmittedProps) {
|
|
317
|
+
omittedProps.types.forEach((type) => {
|
|
318
|
+
if (type.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
319
|
+
type.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
320
|
+
typeof type.literal.value === 'string') {
|
|
321
|
+
omittedPropNames.add(type.literal.value);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
}
|
|
174
325
|
}
|
|
175
326
|
else if (omittedProps.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
|
|
176
327
|
omittedProps.literal.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
177
328
|
typeof omittedProps.literal.value === 'string') {
|
|
329
|
+
canComputeOmittedProps = true;
|
|
178
330
|
omittedPropNames.add(omittedProps.literal.value);
|
|
179
331
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
332
|
+
if (!canComputeOmittedProps) {
|
|
333
|
+
if (spreadKey) {
|
|
334
|
+
recordSpreadMarker();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const added = addBaseTypeProps(baseType);
|
|
338
|
+
if (!added) {
|
|
339
|
+
recordSpreadMarker();
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
185
342
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
343
|
+
const shouldInclude = (name) => !omittedPropNames.has(name);
|
|
344
|
+
let added = false;
|
|
345
|
+
if (currentScope && baseTypeName) {
|
|
346
|
+
const variable = ASTHelpers_1.ASTHelpers.findVariableInScope(currentScope, baseTypeName);
|
|
347
|
+
const definitionNode = variable?.defs[0]?.node;
|
|
348
|
+
if (definitionNode?.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
349
|
+
added = addBaseTypeProps(definitionNode.typeAnnotation, shouldInclude);
|
|
190
350
|
}
|
|
351
|
+
else if (definitionNode?.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
352
|
+
added = addInterfaceProps(definitionNode, currentScope, shouldInclude);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (!added) {
|
|
356
|
+
added = addBaseTypeProps(baseType, shouldInclude);
|
|
191
357
|
}
|
|
358
|
+
if (added) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
recordSpreadMarker();
|
|
192
362
|
};
|
|
193
|
-
|
|
363
|
+
const addPropsFromMembers = (members, shouldInclude = () => true) => {
|
|
364
|
+
let added = false;
|
|
365
|
+
for (const member of members) {
|
|
366
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature &&
|
|
367
|
+
member.type !== utils_1.AST_NODE_TYPES.TSMethodSignature) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const propName = getStaticPropName(member.key);
|
|
371
|
+
if (!propName || !shouldInclude(propName)) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
props[propName] = member.key;
|
|
375
|
+
added = true;
|
|
376
|
+
}
|
|
377
|
+
return added;
|
|
378
|
+
};
|
|
379
|
+
const addInterfaceProps = (declaration, currentScope, shouldInclude = () => true, visitingInterfaces = new Set()) => {
|
|
380
|
+
return (withTypeVisitation(visitingInterfaces, declaration.id.name, () => {
|
|
381
|
+
let added = addPropsFromMembers(declaration.body.body, shouldInclude);
|
|
382
|
+
declaration.extends?.forEach((extension) => {
|
|
383
|
+
if (extension.expression.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
384
|
+
extension.expression.name) {
|
|
385
|
+
const parentVariable = ASTHelpers_1.ASTHelpers.findVariableInScope(currentScope, extension.expression.name);
|
|
386
|
+
const parentDef = parentVariable?.defs[0]?.node;
|
|
387
|
+
if (parentDef?.type ===
|
|
388
|
+
utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
389
|
+
added =
|
|
390
|
+
addInterfaceProps(parentDef, currentScope, shouldInclude, visitingInterfaces) || added;
|
|
391
|
+
}
|
|
392
|
+
else if (parentDef?.type ===
|
|
393
|
+
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
394
|
+
added =
|
|
395
|
+
addBaseTypeProps(parentDef.typeAnnotation, shouldInclude) || added;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
return added;
|
|
400
|
+
}) ?? false);
|
|
401
|
+
};
|
|
402
|
+
function extractProps(typeNode, currentScope) {
|
|
194
403
|
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
195
404
|
typeNode.members.forEach((member) => {
|
|
196
|
-
if (member.type
|
|
197
|
-
|
|
198
|
-
|
|
405
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const propName = getStaticPropName(member.key);
|
|
409
|
+
if (propName) {
|
|
410
|
+
props[propName] = member.key;
|
|
199
411
|
}
|
|
200
412
|
});
|
|
201
413
|
}
|
|
@@ -246,16 +458,15 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
246
458
|
type.typeParameters.params.length === 2) {
|
|
247
459
|
// Handle Omit utility type in intersection
|
|
248
460
|
const [baseType, omittedProps] = type.typeParameters.params;
|
|
249
|
-
handleOmitType(baseType, omittedProps);
|
|
461
|
+
handleOmitType(baseType, omittedProps, currentScope);
|
|
250
462
|
}
|
|
251
463
|
else {
|
|
252
464
|
// For referenced types in intersections, we need to find their type declaration
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
extractProps(variable.defs[0].node.typeAnnotation);
|
|
465
|
+
const typeAliasNode = findTypeAliasDeclaration(typeName.name);
|
|
466
|
+
if (typeAliasNode) {
|
|
467
|
+
withTypeVisitation(visitingTypeReferences, typeName.name, () => {
|
|
468
|
+
extractProps(typeAliasNode.typeAnnotation, currentScope);
|
|
469
|
+
});
|
|
259
470
|
}
|
|
260
471
|
else {
|
|
261
472
|
// If we can't find the type declaration, it's likely an imported type
|
|
@@ -264,15 +475,13 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
264
475
|
props[`...${spreadTypeName}`] = typeName;
|
|
265
476
|
// For imported types, we need to track individual properties that might be used
|
|
266
477
|
// from this spread type, even if we don't know what they are yet
|
|
267
|
-
|
|
268
|
-
spreadTypeProps[spreadTypeName] = [];
|
|
269
|
-
}
|
|
478
|
+
markUnknownSpreadType(spreadTypeProps, spreadTypeName);
|
|
270
479
|
}
|
|
271
480
|
}
|
|
272
481
|
}
|
|
273
482
|
}
|
|
274
483
|
else {
|
|
275
|
-
extractProps(type);
|
|
484
|
+
extractProps(type, currentScope);
|
|
276
485
|
}
|
|
277
486
|
});
|
|
278
487
|
}
|
|
@@ -328,7 +537,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
328
537
|
typeNode.typeParameters.params.length === 2) {
|
|
329
538
|
// Handle Omit<T, K> utility type
|
|
330
539
|
const [baseType, omittedProps] = typeNode.typeParameters.params;
|
|
331
|
-
handleOmitType(baseType, omittedProps);
|
|
540
|
+
handleOmitType(baseType, omittedProps, currentScope);
|
|
332
541
|
}
|
|
333
542
|
else if (
|
|
334
543
|
// Handle other utility types like Required, Partial, etc.
|
|
@@ -339,21 +548,19 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
339
548
|
if (baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
340
549
|
baseType.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
341
550
|
const baseTypeName = baseType.typeName.name;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const variable = scope.variables.find((v) => v.name === baseTypeName);
|
|
345
|
-
if (variable &&
|
|
346
|
-
variable.defs[0]?.node.type ===
|
|
347
|
-
utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
551
|
+
const typeAliasNode = findTypeAliasDeclaration(baseTypeName);
|
|
552
|
+
if (typeAliasNode) {
|
|
348
553
|
// For Partial<T>, Required<T>, etc., add all properties from the base type
|
|
349
|
-
addBaseTypeProps(
|
|
554
|
+
const added = addBaseTypeProps(typeAliasNode.typeAnnotation);
|
|
555
|
+
if (!added) {
|
|
556
|
+
props[`...${baseTypeName}`] = baseType.typeName;
|
|
557
|
+
markUnknownSpreadType(spreadTypeProps, baseTypeName);
|
|
558
|
+
}
|
|
350
559
|
}
|
|
351
560
|
else {
|
|
352
561
|
// If we can't find the base type definition, treat it as a spread type
|
|
353
562
|
props[`...${baseTypeName}`] = baseType.typeName;
|
|
354
|
-
|
|
355
|
-
spreadTypeProps[baseTypeName] = [];
|
|
356
|
-
}
|
|
563
|
+
markUnknownSpreadType(spreadTypeProps, baseTypeName);
|
|
357
564
|
}
|
|
358
565
|
}
|
|
359
566
|
}
|
|
@@ -363,29 +570,24 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
363
570
|
props[`...${spreadTypeName}`] = typeNode.typeName;
|
|
364
571
|
// For imported types, we need to track individual properties that might be used
|
|
365
572
|
// from this spread type, even if we don't know what they are yet
|
|
366
|
-
|
|
367
|
-
spreadTypeProps[spreadTypeName] = [];
|
|
368
|
-
}
|
|
573
|
+
markUnknownSpreadType(spreadTypeProps, spreadTypeName);
|
|
369
574
|
}
|
|
370
575
|
}
|
|
371
576
|
}
|
|
372
577
|
}
|
|
373
|
-
extractProps(node.typeAnnotation);
|
|
578
|
+
extractProps(node.typeAnnotation, scope);
|
|
374
579
|
propsTypes.set(node.id.name, props);
|
|
375
|
-
// Store the mapping of spread types to their properties
|
|
376
580
|
const typeName = node.id.name;
|
|
377
|
-
|
|
378
|
-
// Store the spread type properties for later reference
|
|
581
|
+
componentToReferencedSpreadTypeNames.set(typeName, new Set(Object.keys(spreadTypeProps)));
|
|
379
582
|
for (const [spreadType, propNames] of Object.entries(spreadTypeProps)) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
usedSpreadTypes.set(spreadType, new Set());
|
|
383
|
-
}
|
|
384
|
-
// Add the property names to the spread type's set
|
|
385
|
-
const spreadTypeSet = usedSpreadTypes.get(spreadType);
|
|
583
|
+
const spreadTypeSet = spreadTypeToPropNames.get(spreadType) ?? new Set();
|
|
584
|
+
spreadTypeToPropNames.set(spreadType, spreadTypeSet);
|
|
386
585
|
propNames.forEach((prop) => spreadTypeSet.add(prop));
|
|
387
586
|
}
|
|
388
587
|
}
|
|
588
|
+
finally {
|
|
589
|
+
processingTypeAliases.delete(node.id.name);
|
|
590
|
+
}
|
|
389
591
|
},
|
|
390
592
|
VariableDeclaration(node) {
|
|
391
593
|
if (node.declarations.length === 1) {
|
|
@@ -402,9 +604,11 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
402
604
|
const used = new Set();
|
|
403
605
|
let restUsed = false;
|
|
404
606
|
param.properties.forEach((prop) => {
|
|
405
|
-
if (prop.type === utils_1.AST_NODE_TYPES.Property
|
|
406
|
-
prop.key
|
|
407
|
-
|
|
607
|
+
if (prop.type === utils_1.AST_NODE_TYPES.Property) {
|
|
608
|
+
const propName = getStaticPropName(prop.key);
|
|
609
|
+
if (propName) {
|
|
610
|
+
used.add(propName);
|
|
611
|
+
}
|
|
408
612
|
}
|
|
409
613
|
else if (prop.type === utils_1.AST_NODE_TYPES.RestElement &&
|
|
410
614
|
prop.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
@@ -17,7 +17,7 @@ exports.noUnusedUseState = createRule({
|
|
|
17
17
|
},
|
|
18
18
|
fixable: 'code',
|
|
19
19
|
messages: {
|
|
20
|
-
unusedUseState: '
|
|
20
|
+
unusedUseState: 'State value "{{stateName}}" from useState is discarded. React still allocates state and re-renders for a value you never read, which misleads readers into thinking the component depends on that state. Remove the useState pair or switch to a ref/derived value when you only need the setter-style side effect.',
|
|
21
21
|
},
|
|
22
22
|
schema: [],
|
|
23
23
|
},
|
|
@@ -41,12 +41,16 @@ exports.noUnusedUseState = createRule({
|
|
|
41
41
|
utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
|
|
42
42
|
(arrayPattern.elements[0].name === '_' ||
|
|
43
43
|
arrayPattern.elements[0].name.startsWith('_'))) {
|
|
44
|
+
const stateIdentifier = arrayPattern.elements[0];
|
|
44
45
|
context.report({
|
|
45
46
|
node,
|
|
46
47
|
messageId: 'unusedUseState',
|
|
48
|
+
data: {
|
|
49
|
+
stateName: stateIdentifier.name,
|
|
50
|
+
},
|
|
47
51
|
fix: (fixer) => {
|
|
48
52
|
// Remove the entire useState declaration
|
|
49
|
-
const sourceCode = context.
|
|
53
|
+
const sourceCode = context.sourceCode;
|
|
50
54
|
const parentStatement = node.parent;
|
|
51
55
|
if (parentStatement &&
|
|
52
56
|
parentStatement.type ===
|
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noUselessFragment = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Normalizes JSX child node types into short descriptors used inside lint messages.
|
|
6
|
+
* Keeps message phrasing consistent regardless of the specific child node shape.
|
|
7
|
+
* @param child - The JSX child node to describe.
|
|
8
|
+
* @returns Human-readable descriptor for the child type used in lint messages.
|
|
9
|
+
*/
|
|
10
|
+
const describeChild = (child) => {
|
|
11
|
+
switch (child.type) {
|
|
12
|
+
case 'JSXElement':
|
|
13
|
+
return 'JSX element';
|
|
14
|
+
case 'JSXFragment':
|
|
15
|
+
return 'fragment';
|
|
16
|
+
case 'JSXExpressionContainer':
|
|
17
|
+
return 'expression result';
|
|
18
|
+
case 'JSXText':
|
|
19
|
+
return 'text node';
|
|
20
|
+
case 'JSXSpreadChild':
|
|
21
|
+
return 'spread child';
|
|
22
|
+
default:
|
|
23
|
+
return 'child node';
|
|
24
|
+
}
|
|
25
|
+
};
|
|
4
26
|
exports.noUselessFragment = {
|
|
5
27
|
create(context) {
|
|
6
28
|
return {
|
|
7
29
|
JSXFragment(node) {
|
|
8
30
|
if (node.children.length === 1) {
|
|
31
|
+
const [child] = node.children;
|
|
9
32
|
context.report({
|
|
10
33
|
node,
|
|
11
34
|
messageId: 'noUselessFragment',
|
|
35
|
+
data: {
|
|
36
|
+
childKind: describeChild(child),
|
|
37
|
+
},
|
|
12
38
|
fix(fixer) {
|
|
13
|
-
const sourceCode = context.
|
|
39
|
+
const sourceCode = context.sourceCode;
|
|
14
40
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
15
41
|
const openingFragment = sourceCode.getFirstToken(node);
|
|
16
42
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -38,7 +64,7 @@ exports.noUselessFragment = {
|
|
|
38
64
|
recommended: 'error',
|
|
39
65
|
},
|
|
40
66
|
messages: {
|
|
41
|
-
noUselessFragment: 'React fragment
|
|
67
|
+
noUselessFragment: 'React fragment wraps a single {{childKind}} and does not provide grouping. Fragments exist to wrap multiple siblings; leaving this fragment adds extra syntax and a React tree node without changing the rendered output. Remove the fragment and return the child directly.',
|
|
42
68
|
},
|
|
43
69
|
schema: [],
|
|
44
70
|
fixable: 'code',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
ignoreCallExpressions?: boolean;
|
|
4
|
+
ignoreSymbol?: boolean;
|
|
5
|
+
tsOnly?: boolean;
|
|
6
|
+
}
|
|
7
|
+
];
|
|
8
|
+
export declare const noUselessUsememoPrimitives: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"uselessUseMemoPrimitive", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
9
|
+
export default noUselessUsememoPrimitives;
|