@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
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireMemoizeJsxReturners = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
const MEMOIZE_PREFERRED_MODULE = '@blumintinc/typescript-memoize';
|
|
7
|
+
const MEMOIZE_MODULES = new Set([
|
|
8
|
+
MEMOIZE_PREFERRED_MODULE,
|
|
9
|
+
'typescript-memoize',
|
|
10
|
+
]);
|
|
11
|
+
function isMemoizeDecorator(decorator, alias, namespaceAlias) {
|
|
12
|
+
const expression = decorator.expression;
|
|
13
|
+
const matchesAliasIdentifier = (node) => !!node && node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === alias;
|
|
14
|
+
const matchesNamespaceMember = (node) => {
|
|
15
|
+
if (node.computed)
|
|
16
|
+
return false;
|
|
17
|
+
if (node.property.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
18
|
+
return false;
|
|
19
|
+
if (node.property.name !== 'Memoize')
|
|
20
|
+
return false;
|
|
21
|
+
return (!!namespaceAlias &&
|
|
22
|
+
node.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
23
|
+
node.object.name === namespaceAlias);
|
|
24
|
+
};
|
|
25
|
+
if (expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
26
|
+
const { callee } = expression;
|
|
27
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
28
|
+
matchesAliasIdentifier(callee)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
32
|
+
matchesNamespaceMember(callee)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (matchesAliasIdentifier(expression)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
40
|
+
matchesNamespaceMember(expression)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
function getMemberName(node) {
|
|
46
|
+
const key = node.key;
|
|
47
|
+
if (key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
48
|
+
return key.name;
|
|
49
|
+
}
|
|
50
|
+
if (key.type === utils_1.AST_NODE_TYPES.Literal && typeof key.value === 'string') {
|
|
51
|
+
return key.value;
|
|
52
|
+
}
|
|
53
|
+
if (key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
|
|
54
|
+
return key.name;
|
|
55
|
+
}
|
|
56
|
+
return 'member';
|
|
57
|
+
}
|
|
58
|
+
function extractFunctionsFromVariableDeclaration(declaration, functions) {
|
|
59
|
+
for (const declarator of declaration.declarations) {
|
|
60
|
+
if (declarator.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const init = declarator.init;
|
|
64
|
+
if (!init ||
|
|
65
|
+
(init.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
66
|
+
init.type !== utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
functions.set(declarator.id.name, init);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function traverseStatements(statements, functions) {
|
|
73
|
+
statements.forEach((statement) => visitStatement(statement, functions));
|
|
74
|
+
}
|
|
75
|
+
function visitStatementByType(statement, functions) {
|
|
76
|
+
switch (statement.type) {
|
|
77
|
+
case utils_1.AST_NODE_TYPES.BlockStatement:
|
|
78
|
+
traverseStatements(statement.body, functions);
|
|
79
|
+
break;
|
|
80
|
+
case utils_1.AST_NODE_TYPES.IfStatement:
|
|
81
|
+
visitStatement(statement.consequent, functions);
|
|
82
|
+
if (statement.alternate) {
|
|
83
|
+
visitStatement(statement.alternate, functions);
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case utils_1.AST_NODE_TYPES.SwitchStatement:
|
|
87
|
+
statement.cases.forEach((caseNode) => traverseStatements(caseNode.consequent, functions));
|
|
88
|
+
break;
|
|
89
|
+
case utils_1.AST_NODE_TYPES.TryStatement:
|
|
90
|
+
visitStatement(statement.block, functions);
|
|
91
|
+
if (statement.handler) {
|
|
92
|
+
visitStatement(statement.handler.body, functions);
|
|
93
|
+
}
|
|
94
|
+
if (statement.finalizer) {
|
|
95
|
+
visitStatement(statement.finalizer, functions);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
case utils_1.AST_NODE_TYPES.ForStatement:
|
|
99
|
+
if (statement.init &&
|
|
100
|
+
statement.init.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
101
|
+
extractFunctionsFromVariableDeclaration(statement.init, functions);
|
|
102
|
+
}
|
|
103
|
+
visitStatement(statement.body, functions);
|
|
104
|
+
break;
|
|
105
|
+
case utils_1.AST_NODE_TYPES.ForInStatement:
|
|
106
|
+
case utils_1.AST_NODE_TYPES.ForOfStatement:
|
|
107
|
+
if (statement.left.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
108
|
+
extractFunctionsFromVariableDeclaration(statement.left, functions);
|
|
109
|
+
}
|
|
110
|
+
visitStatement(statement.body, functions);
|
|
111
|
+
break;
|
|
112
|
+
case utils_1.AST_NODE_TYPES.WhileStatement:
|
|
113
|
+
case utils_1.AST_NODE_TYPES.DoWhileStatement:
|
|
114
|
+
case utils_1.AST_NODE_TYPES.LabeledStatement:
|
|
115
|
+
case utils_1.AST_NODE_TYPES.WithStatement:
|
|
116
|
+
visitStatement(statement.body, functions);
|
|
117
|
+
break;
|
|
118
|
+
default:
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function visitStatement(statement, functions) {
|
|
123
|
+
if (statement.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
124
|
+
statement.id?.name) {
|
|
125
|
+
functions.set(statement.id.name, statement);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
129
|
+
extractFunctionsFromVariableDeclaration(statement, functions);
|
|
130
|
+
}
|
|
131
|
+
visitStatementByType(statement, functions);
|
|
132
|
+
}
|
|
133
|
+
function collectLocalFunctions(body) {
|
|
134
|
+
const functions = new Map();
|
|
135
|
+
if (!body || body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
136
|
+
return functions;
|
|
137
|
+
}
|
|
138
|
+
traverseStatements(body.body, functions);
|
|
139
|
+
return functions;
|
|
140
|
+
}
|
|
141
|
+
/** Type guard for ParenthesizedExpression (non-standard node type in some parsers). */
|
|
142
|
+
function isParenthesizedExpression(node) {
|
|
143
|
+
return (!!node &&
|
|
144
|
+
node.type === 'ParenthesizedExpression' &&
|
|
145
|
+
'expression' in node &&
|
|
146
|
+
node.expression != null);
|
|
147
|
+
}
|
|
148
|
+
function isIdentifierReturningJsx(node, knownFunctions, cache, factoryContext) {
|
|
149
|
+
const targetFn = knownFunctions.get(node.name);
|
|
150
|
+
return (!!targetFn &&
|
|
151
|
+
functionReturnsJSX(targetFn, knownFunctions, cache, factoryContext));
|
|
152
|
+
}
|
|
153
|
+
function isWrappedReturningJsx(node, knownFunctions, cache, factoryContext) {
|
|
154
|
+
return expressionReturnsJSX(node.expression, knownFunctions, cache, factoryContext);
|
|
155
|
+
}
|
|
156
|
+
function isSequenceReturningJsx(node, knownFunctions, cache, factoryContext) {
|
|
157
|
+
return (node.expressions.length > 0 &&
|
|
158
|
+
expressionReturnsJSX(node.expressions[node.expressions.length - 1], knownFunctions, cache, factoryContext));
|
|
159
|
+
}
|
|
160
|
+
function isLogicalReturningJsx(node, knownFunctions, cache, factoryContext) {
|
|
161
|
+
return (expressionReturnsJSX(node.left, knownFunctions, cache, factoryContext) ||
|
|
162
|
+
expressionReturnsJSX(node.right, knownFunctions, cache, factoryContext));
|
|
163
|
+
}
|
|
164
|
+
function isConditionalReturningJsx(node, knownFunctions, cache, factoryContext) {
|
|
165
|
+
return (expressionReturnsJSX(node.consequent, knownFunctions, cache, factoryContext) ||
|
|
166
|
+
expressionReturnsJSX(node.alternate, knownFunctions, cache, factoryContext));
|
|
167
|
+
}
|
|
168
|
+
function callExpressionReturnsJSX(expression, knownFunctions, cache, factoryContext) {
|
|
169
|
+
const { callee } = expression;
|
|
170
|
+
const firstNonSpreadArgument = expression.arguments.find((arg) => arg.type !== utils_1.AST_NODE_TYPES.SpreadElement);
|
|
171
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
172
|
+
if (factoryContext.reactCreateElementIdentifiers.has(callee.name)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (isIdentifierReturningJsx(callee, knownFunctions, cache, factoryContext)) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (factoryContext.reactMemoIdentifiers.has(callee.name) &&
|
|
179
|
+
firstNonSpreadArgument &&
|
|
180
|
+
expressionReturnsJSX(firstNonSpreadArgument, knownFunctions, cache, factoryContext)) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
185
|
+
!callee.computed &&
|
|
186
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
187
|
+
const propertyName = callee.property.name;
|
|
188
|
+
/** Treat React.createElement style calls as JSX-producing. */
|
|
189
|
+
if (propertyName === 'createElement' &&
|
|
190
|
+
callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
191
|
+
factoryContext.reactNamespaceIdentifiers.has(callee.object.name)) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (propertyName === 'memo' &&
|
|
195
|
+
callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
196
|
+
factoryContext.reactNamespaceIdentifiers.has(callee.object.name) &&
|
|
197
|
+
firstNonSpreadArgument &&
|
|
198
|
+
expressionReturnsJSX(firstNonSpreadArgument, knownFunctions, cache, factoryContext)) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
if ((propertyName === 'call' || propertyName === 'apply') &&
|
|
202
|
+
callee.object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
203
|
+
if (isIdentifierReturningJsx(callee.object, knownFunctions, cache, factoryContext)) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function isFunctionReturningJSX(node, knownFunctions, cache, factoryContext) {
|
|
211
|
+
return functionReturnsJSX(node, knownFunctions, cache, factoryContext);
|
|
212
|
+
}
|
|
213
|
+
function dispatchExpressionReturnsJSX(expression, knownFunctions, cache, factoryContext) {
|
|
214
|
+
const type = expression.type;
|
|
215
|
+
switch (type) {
|
|
216
|
+
case utils_1.AST_NODE_TYPES.JSXElement:
|
|
217
|
+
case utils_1.AST_NODE_TYPES.JSXFragment:
|
|
218
|
+
return true;
|
|
219
|
+
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
220
|
+
case utils_1.AST_NODE_TYPES.FunctionExpression:
|
|
221
|
+
return isFunctionReturningJSX(expression, knownFunctions, cache, factoryContext);
|
|
222
|
+
case utils_1.AST_NODE_TYPES.Identifier:
|
|
223
|
+
return isIdentifierReturningJsx(expression, knownFunctions, cache, factoryContext);
|
|
224
|
+
case utils_1.AST_NODE_TYPES.CallExpression:
|
|
225
|
+
return callExpressionReturnsJSX(expression, knownFunctions, cache, factoryContext);
|
|
226
|
+
case utils_1.AST_NODE_TYPES.ConditionalExpression:
|
|
227
|
+
return isConditionalReturningJsx(expression, knownFunctions, cache, factoryContext);
|
|
228
|
+
case utils_1.AST_NODE_TYPES.LogicalExpression:
|
|
229
|
+
return isLogicalReturningJsx(expression, knownFunctions, cache, factoryContext);
|
|
230
|
+
case utils_1.AST_NODE_TYPES.SequenceExpression:
|
|
231
|
+
return isSequenceReturningJsx(expression, knownFunctions, cache, factoryContext);
|
|
232
|
+
case utils_1.AST_NODE_TYPES.TSAsExpression:
|
|
233
|
+
case utils_1.AST_NODE_TYPES.TSTypeAssertion:
|
|
234
|
+
case utils_1.AST_NODE_TYPES.TSNonNullExpression:
|
|
235
|
+
case utils_1.AST_NODE_TYPES.TSSatisfiesExpression:
|
|
236
|
+
case utils_1.AST_NODE_TYPES.ChainExpression:
|
|
237
|
+
return isWrappedReturningJsx(expression, knownFunctions, cache, factoryContext);
|
|
238
|
+
default:
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function expressionReturnsJSX(expression, knownFunctions, cache, factoryContext) {
|
|
243
|
+
if (!expression)
|
|
244
|
+
return false;
|
|
245
|
+
if (isParenthesizedExpression(expression)) {
|
|
246
|
+
return expressionReturnsJSX(expression.expression, knownFunctions, cache, factoryContext);
|
|
247
|
+
}
|
|
248
|
+
return dispatchExpressionReturnsJSX(expression, knownFunctions, cache, factoryContext);
|
|
249
|
+
}
|
|
250
|
+
function statementReturnsJSX(statement, knownFunctions, cache, factoryContext) {
|
|
251
|
+
switch (statement.type) {
|
|
252
|
+
case utils_1.AST_NODE_TYPES.ReturnStatement:
|
|
253
|
+
return expressionReturnsJSX(statement.argument, knownFunctions, cache, factoryContext);
|
|
254
|
+
case utils_1.AST_NODE_TYPES.BlockStatement:
|
|
255
|
+
return statement.body.some((child) => statementReturnsJSX(child, knownFunctions, cache, factoryContext));
|
|
256
|
+
case utils_1.AST_NODE_TYPES.IfStatement:
|
|
257
|
+
return (statementReturnsJSX(statement.consequent, knownFunctions, cache, factoryContext) ||
|
|
258
|
+
(statement.alternate
|
|
259
|
+
? statementReturnsJSX(statement.alternate, knownFunctions, cache, factoryContext)
|
|
260
|
+
: false));
|
|
261
|
+
case utils_1.AST_NODE_TYPES.SwitchStatement:
|
|
262
|
+
return statement.cases.some((caseNode) => caseNode.consequent.some((consequent) => statementReturnsJSX(consequent, knownFunctions, cache, factoryContext)));
|
|
263
|
+
case utils_1.AST_NODE_TYPES.TryStatement:
|
|
264
|
+
if (statementReturnsJSX(statement.block, knownFunctions, cache, factoryContext)) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
if (statement.handler &&
|
|
268
|
+
statementReturnsJSX(statement.handler.body, knownFunctions, cache, factoryContext)) {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
if (statement.finalizer &&
|
|
272
|
+
statementReturnsJSX(statement.finalizer, knownFunctions, cache, factoryContext)) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
case utils_1.AST_NODE_TYPES.ForStatement:
|
|
277
|
+
case utils_1.AST_NODE_TYPES.ForInStatement:
|
|
278
|
+
case utils_1.AST_NODE_TYPES.ForOfStatement:
|
|
279
|
+
case utils_1.AST_NODE_TYPES.WhileStatement:
|
|
280
|
+
case utils_1.AST_NODE_TYPES.DoWhileStatement:
|
|
281
|
+
case utils_1.AST_NODE_TYPES.LabeledStatement:
|
|
282
|
+
case utils_1.AST_NODE_TYPES.WithStatement:
|
|
283
|
+
return statementReturnsJSX(statement.body, knownFunctions, cache, factoryContext);
|
|
284
|
+
default:
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function functionReturnsJSX(fn, knownFunctions, cache, factoryContext) {
|
|
289
|
+
const cached = cache.get(fn);
|
|
290
|
+
if (cached === true || cached === false) {
|
|
291
|
+
return cached;
|
|
292
|
+
}
|
|
293
|
+
if (cached === 'pending') {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
cache.set(fn, 'pending');
|
|
297
|
+
if (fn.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
298
|
+
fn.type !== utils_1.AST_NODE_TYPES.FunctionExpression &&
|
|
299
|
+
fn.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
300
|
+
cache.set(fn, false);
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
let extendedFunctions = knownFunctions;
|
|
304
|
+
if (fn.body && fn.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
305
|
+
const nested = collectLocalFunctions(fn.body);
|
|
306
|
+
if (nested.size > 0) {
|
|
307
|
+
extendedFunctions = new Map(knownFunctions);
|
|
308
|
+
for (const [name, nestedFn] of nested.entries()) {
|
|
309
|
+
extendedFunctions.set(name, nestedFn);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
let returnsJSX = false;
|
|
314
|
+
if (!fn.body) {
|
|
315
|
+
cache.set(fn, false);
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
if (fn.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
319
|
+
returnsJSX = statementReturnsJSX(fn.body, extendedFunctions, cache, factoryContext);
|
|
320
|
+
}
|
|
321
|
+
else if (expressionReturnsJSX(fn.body, extendedFunctions, cache, factoryContext)) {
|
|
322
|
+
returnsJSX = true;
|
|
323
|
+
}
|
|
324
|
+
cache.set(fn, returnsJSX);
|
|
325
|
+
return returnsJSX;
|
|
326
|
+
}
|
|
327
|
+
function getImportFixes(fixer, sourceCode, hasMemoizeImport, scheduledImportFix) {
|
|
328
|
+
const fixes = [];
|
|
329
|
+
if (hasMemoizeImport || scheduledImportFix) {
|
|
330
|
+
return { fixes, scheduledImportFix };
|
|
331
|
+
}
|
|
332
|
+
const programBody = sourceCode.ast.body;
|
|
333
|
+
/** Look for an existing import from the memoize module. */
|
|
334
|
+
const existingMemoizeImport = programBody.find((statement) => statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
|
|
335
|
+
MEMOIZE_MODULES.has(String(statement.source.value)));
|
|
336
|
+
if (existingMemoizeImport &&
|
|
337
|
+
existingMemoizeImport.specifiers.some((s) => s.type === utils_1.AST_NODE_TYPES.ImportSpecifier)) {
|
|
338
|
+
/** Augment existing named import. */
|
|
339
|
+
const lastSpecifier = [...existingMemoizeImport.specifiers]
|
|
340
|
+
.reverse()
|
|
341
|
+
.find((s) => s.type === utils_1.AST_NODE_TYPES.ImportSpecifier);
|
|
342
|
+
if (lastSpecifier) {
|
|
343
|
+
fixes.push(fixer.insertTextAfter(lastSpecifier, ', Memoize'));
|
|
344
|
+
return { fixes, scheduledImportFix: true };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const firstImport = programBody.find((statement) => statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
|
|
348
|
+
const anchorNode = (firstImport ?? programBody[0]);
|
|
349
|
+
if (anchorNode) {
|
|
350
|
+
const text = sourceCode.text;
|
|
351
|
+
const anchorStart = anchorNode.range?.[0] ?? 0;
|
|
352
|
+
const lineStart = text.lastIndexOf('\n', anchorStart - 1) + 1;
|
|
353
|
+
const leadingWhitespace = text.slice(lineStart, anchorStart).match(/^[ \t]*/)?.[0] ?? '';
|
|
354
|
+
const importLine = `${leadingWhitespace}import { Memoize } from '${MEMOIZE_PREFERRED_MODULE}';\n`;
|
|
355
|
+
fixes.push(fixer.insertTextBeforeRange([lineStart, lineStart], importLine));
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
fixes.push(fixer.insertTextBeforeRange([0, 0], `import { Memoize } from '${MEMOIZE_PREFERRED_MODULE}';\n`));
|
|
359
|
+
}
|
|
360
|
+
return { fixes, scheduledImportFix: true };
|
|
361
|
+
}
|
|
362
|
+
exports.requireMemoizeJsxReturners = (0, createRule_1.createRule)({
|
|
363
|
+
name: 'require-memoize-jsx-returners',
|
|
364
|
+
meta: {
|
|
365
|
+
type: 'suggestion',
|
|
366
|
+
docs: {
|
|
367
|
+
description: 'Require @Memoize() decorator on instance members that return JSX or JSX factories',
|
|
368
|
+
recommended: 'error',
|
|
369
|
+
},
|
|
370
|
+
fixable: 'code',
|
|
371
|
+
schema: [],
|
|
372
|
+
messages: {
|
|
373
|
+
requireMemoizeJsxReturner: '"{{name}}" returns JSX (or a JSX-producing factory) without @Memoize() → Each call/access creates a new component/function reference that can trigger avoidable React re-renders or remounts → Add @Memoize() to "{{name}}" and import { Memoize } from "@blumintinc/typescript-memoize".',
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
defaultOptions: [],
|
|
377
|
+
create(context) {
|
|
378
|
+
const filename = context.getFilename();
|
|
379
|
+
const isVirtualFile = filename.startsWith('<');
|
|
380
|
+
if (!isVirtualFile && !/\.tsx?$/i.test(filename)) {
|
|
381
|
+
return {};
|
|
382
|
+
}
|
|
383
|
+
let hasMemoizeImport = false;
|
|
384
|
+
let memoizeAlias = 'Memoize';
|
|
385
|
+
let memoizeNamespace = null;
|
|
386
|
+
let scheduledImportFix = false;
|
|
387
|
+
const jsxReturnCache = new WeakMap();
|
|
388
|
+
const reactMemoIdentifiers = new Set();
|
|
389
|
+
const reactNamespaceIdentifiers = new Set();
|
|
390
|
+
const reactCreateElementIdentifiers = new Set();
|
|
391
|
+
const factoryContext = {
|
|
392
|
+
reactMemoIdentifiers,
|
|
393
|
+
reactNamespaceIdentifiers,
|
|
394
|
+
reactCreateElementIdentifiers,
|
|
395
|
+
};
|
|
396
|
+
return {
|
|
397
|
+
ImportDeclaration(node) {
|
|
398
|
+
const sourceValue = String(node.source.value);
|
|
399
|
+
if (sourceValue === 'react') {
|
|
400
|
+
node.specifiers.forEach((specifier) => {
|
|
401
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
402
|
+
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
403
|
+
specifier.imported.name === 'memo') {
|
|
404
|
+
reactMemoIdentifiers.add(specifier.local?.name ?? specifier.imported.name);
|
|
405
|
+
}
|
|
406
|
+
else if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
407
|
+
specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
408
|
+
specifier.imported.name === 'createElement') {
|
|
409
|
+
reactCreateElementIdentifiers.add(specifier.local?.name ?? specifier.imported.name);
|
|
410
|
+
}
|
|
411
|
+
else if (specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier) {
|
|
412
|
+
reactNamespaceIdentifiers.add(specifier.local.name);
|
|
413
|
+
}
|
|
414
|
+
else if (specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
415
|
+
reactNamespaceIdentifiers.add(specifier.local.name);
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (!MEMOIZE_MODULES.has(sourceValue)) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
for (const specifier of node.specifiers) {
|
|
423
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
|
|
424
|
+
if (specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
425
|
+
specifier.imported.name === 'Memoize') {
|
|
426
|
+
hasMemoizeImport = true;
|
|
427
|
+
memoizeAlias = specifier.local?.name ?? memoizeAlias;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else if (specifier.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
431
|
+
hasMemoizeImport = true;
|
|
432
|
+
memoizeNamespace = specifier.local.name;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
MethodDefinition(node) {
|
|
437
|
+
if (node.kind === 'set' || node.kind === 'constructor') {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (node.static) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (node.value.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const hasDecorator = node.decorators?.some((decorator) => isMemoizeDecorator(decorator, memoizeAlias, memoizeNamespace));
|
|
447
|
+
if (hasDecorator) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const localFunctions = collectLocalFunctions(node.value.body);
|
|
451
|
+
if (!functionReturnsJSX(node.value, localFunctions, jsxReturnCache, factoryContext)) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const decoratorIdent = memoizeNamespace
|
|
455
|
+
? `${memoizeNamespace}.Memoize`
|
|
456
|
+
: memoizeAlias;
|
|
457
|
+
context.report({
|
|
458
|
+
node,
|
|
459
|
+
messageId: 'requireMemoizeJsxReturner',
|
|
460
|
+
data: { name: getMemberName(node) },
|
|
461
|
+
fix(fixer) {
|
|
462
|
+
const sourceCode = context.getSourceCode();
|
|
463
|
+
const { fixes, scheduledImportFix: newScheduledImportFix } = getImportFixes(fixer, sourceCode, hasMemoizeImport, scheduledImportFix);
|
|
464
|
+
scheduledImportFix = newScheduledImportFix;
|
|
465
|
+
const insertionTarget = node.decorators && node.decorators.length > 0
|
|
466
|
+
? node.decorators[0]
|
|
467
|
+
: node;
|
|
468
|
+
const insertionStart = insertionTarget.range
|
|
469
|
+
? insertionTarget.range[0]
|
|
470
|
+
: node.range
|
|
471
|
+
? node.range[0]
|
|
472
|
+
: 0;
|
|
473
|
+
const text = sourceCode.text;
|
|
474
|
+
const lineStart = text.lastIndexOf('\n', insertionStart - 1) + 1;
|
|
475
|
+
const leadingWhitespace = text.slice(lineStart, insertionStart).match(/^[ \t]*/)?.[0] ?? '';
|
|
476
|
+
fixes.push(fixer.insertTextBeforeRange([lineStart, lineStart], `${leadingWhitespace}@${decoratorIdent}()\n`));
|
|
477
|
+
return fixes;
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
exports.default = exports.requireMemoizeJsxReturners;
|
|
485
|
+
//# sourceMappingURL=require-memoize-jsx-returners.js.map
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.requireUseMemoObjectLiterals = void 0;
|
|
4
4
|
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
const ASTHelpers_1 = require("../utils/ASTHelpers");
|
|
5
6
|
exports.requireUseMemoObjectLiterals = (0, createRule_1.createRule)({
|
|
6
7
|
name: 'require-usememo-object-literals',
|
|
7
8
|
meta: {
|
|
@@ -34,9 +35,7 @@ exports.requireUseMemoObjectLiterals = (0, createRule_1.createRule)({
|
|
|
34
35
|
if ((expression.type === 'ObjectExpression' ||
|
|
35
36
|
expression.type === 'ArrayExpression') &&
|
|
36
37
|
// Ensure we're in a function component context
|
|
37
|
-
context
|
|
38
|
-
.getAncestors()
|
|
39
|
-
.some((ancestor) => ancestor.type === 'FunctionDeclaration' ||
|
|
38
|
+
ASTHelpers_1.ASTHelpers.getAncestors(context, node).some((ancestor) => ancestor.type === 'FunctionDeclaration' ||
|
|
40
39
|
ancestor.type === 'ArrowFunctionExpression' ||
|
|
41
40
|
ancestor.type === 'FunctionExpression')) {
|
|
42
41
|
// Check if the parent component name starts with an uppercase letter
|
|
@@ -14,7 +14,7 @@ exports.testFileLocationEnforcement = (0, createRule_1.createRule)({
|
|
|
14
14
|
meta: {
|
|
15
15
|
type: 'suggestion',
|
|
16
16
|
docs: {
|
|
17
|
-
description: 'Enforce colocating *.test.ts
|
|
17
|
+
description: 'Enforce colocating *.test.ts or *.test.tsx files with the code they cover.',
|
|
18
18
|
recommended: 'error',
|
|
19
19
|
},
|
|
20
20
|
schema: [],
|
|
@@ -28,7 +28,7 @@ exports.useLatestCallback = (0, createRule_1.createRule)({
|
|
|
28
28
|
let useLatestCallbackImportName = 'useLatestCallback';
|
|
29
29
|
// Track if any useCallback calls should be replaced
|
|
30
30
|
let hasNonJsxUseCallbacks = false;
|
|
31
|
-
|
|
31
|
+
const useCallbackLocalNames = new Set();
|
|
32
32
|
const reactNamespaceNames = new Set();
|
|
33
33
|
const reactDefaultLikeNames = new Set();
|
|
34
34
|
let hasReactMemberUseCallback = false;
|
|
@@ -55,7 +55,7 @@ exports.useLatestCallback = (0, createRule_1.createRule)({
|
|
|
55
55
|
return (isJSXLikeReturn(node.consequent) || isJSXLikeReturn(node.alternate));
|
|
56
56
|
}
|
|
57
57
|
if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
|
|
58
|
-
return
|
|
58
|
+
return isJSXLikeReturn(node.left) || isJSXLikeReturn(node.right);
|
|
59
59
|
}
|
|
60
60
|
if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
61
61
|
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
@@ -127,7 +127,8 @@ exports.useLatestCallback = (0, createRule_1.createRule)({
|
|
|
127
127
|
reactDefaultLikeNames.has(node.callee.object.name) &&
|
|
128
128
|
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
129
129
|
node.callee.property.name === 'useCallback';
|
|
130
|
-
if ((isUseCallbackIdentifierCall || isUseCallbackMemberCall) &&
|
|
130
|
+
if ((isUseCallbackIdentifierCall || isUseCallbackMemberCall) &&
|
|
131
|
+
node.arguments.length >= 1) {
|
|
131
132
|
if (isUseCallbackMemberCall) {
|
|
132
133
|
hasReactMemberUseCallback = true;
|
|
133
134
|
}
|
|
@@ -175,7 +176,7 @@ exports.useLatestCallback = (0, createRule_1.createRule)({
|
|
|
175
176
|
recommendedHook: replacementName,
|
|
176
177
|
},
|
|
177
178
|
fix(fixer) {
|
|
178
|
-
const sourceCode = context.
|
|
179
|
+
const sourceCode = context.sourceCode;
|
|
179
180
|
const callbackText = sourceCode.getText(node.arguments[0]);
|
|
180
181
|
const typeParams = node.typeParameters
|
|
181
182
|
? sourceCode.getText(node.typeParameters)
|
|
@@ -192,7 +193,7 @@ exports.useLatestCallback = (0, createRule_1.createRule)({
|
|
|
192
193
|
if (!hasNonJsxUseCallbacks) {
|
|
193
194
|
return; // Don't modify imports if all useCallback calls return JSX
|
|
194
195
|
}
|
|
195
|
-
const sourceCode = context.
|
|
196
|
+
const sourceCode = context.sourceCode;
|
|
196
197
|
const program = sourceCode.ast;
|
|
197
198
|
for (const statement of program.body) {
|
|
198
199
|
if (statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type GroupName = 'event-handlers' | 'utilities' | 'other';
|
|
3
|
+
type DependencyDirection = 'callers-first' | 'callees-first';
|
|
4
|
+
type ExportPlacement = 'top' | 'bottom' | 'ignore';
|
|
5
|
+
type Options = [
|
|
6
|
+
Partial<{
|
|
7
|
+
exportPlacement: ExportPlacement;
|
|
8
|
+
dependencyDirection: DependencyDirection;
|
|
9
|
+
groupOrder: GroupName[];
|
|
10
|
+
eventHandlerPattern: string;
|
|
11
|
+
utilityPattern: string;
|
|
12
|
+
}>
|
|
13
|
+
];
|
|
14
|
+
type MessageIds = 'misorderedFunction';
|
|
15
|
+
export declare const verticallyGroupRelatedFunctions: TSESLint.RuleModule<MessageIds, Options>;
|
|
16
|
+
export {};
|