Skip to content

Commit 5bf7397

Browse files
author
Arthur Geron
committed
fix: autofix not working for jsx expressions
1 parent a8296be commit 5bf7397

File tree

3 files changed

+27
-12
lines changed

3 files changed

+27
-12
lines changed

__tests__/require-usememo.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,19 @@ describe('Rule - Require-usememo', () => {
291291
},
292292
],
293293
invalid: [
294+
{
295+
code: `
296+
const Component = () => {
297+
return <Child prop={<SomeComponent />} />;
298+
}`,
299+
errors: [{ messageId: "jsx-usememo-props" }],
300+
output: `import { useMemo } from 'react';
301+
302+
const Component = () => {
303+
const prop = useMemo(() => (<SomeComponent />), []);
304+
return <Child prop={prop} />;
305+
}`,
306+
},
294307
{
295308
code: `
296309
const Component = () => {
@@ -514,7 +527,7 @@ describe('Rule - Require-usememo', () => {
514527
output: `import { useMemo } from 'react';
515528
516529
function Component() {
517-
const component = useMemo(() => <OtherChild />, []);
530+
const component = useMemo(() => (<OtherChild />), []);
518531
return <Child component={component} />;
519532
}`
520533
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@arthurgeron/eslint-plugin-react-usememo",
3-
"version": "2.1.1",
3+
"version": "2.1.2",
44
"description": "",
55
"main": "dist/index.js",
66
"author": "Stefano J. Attardi <[email protected]> & Arthur Geron <[email protected]",

src/require-usememo/utils.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,12 @@ function getSafeVariableName(context: Rule.RuleContext, name: string) {
128128
}
129129

130130
// Eslint Auto-fix logic, functional components/hooks only
131-
export function fixBasedOnMessageId(node: Rule.Node, messageId: keyof typeof MessagesRequireUseMemo, fixer: Rule.RuleFixer, context: Rule.RuleContext, reactImportData: ReactImportInformation) {
131+
export function fixBasedOnMessageId(node: Rule.Node | TSESTree.JSXElement, messageId: keyof typeof MessagesRequireUseMemo, fixer: Rule.RuleFixer, context: Rule.RuleContext, reactImportData: ReactImportInformation) {
132132
const sourceCode = context.getSourceCode();
133133
let hook = messageIdToHookDict[messageId] || 'useMemo';
134134
const isObjExpression = node.type === 'ObjectExpression';
135-
const parentIsVariableDeclarator = node.parent.type === 'VariableDeclarator';
135+
const isJSXElement = node.type === 'JSXElement';
136+
const parentIsVariableDeclarator = (node as Rule.Node).parent.type === 'VariableDeclarator';
136137
const isArrowFunctionExpression = node.type === 'ArrowFunctionExpression';
137138
const isFunctionExpression = node.type === 'FunctionExpression';
138139
const isCorrectableFunctionExpression = isFunctionExpression || (isArrowFunctionExpression && parentIsVariableDeclarator);
@@ -142,8 +143,9 @@ export function fixBasedOnMessageId(node: Rule.Node, messageId: keyof typeof Mes
142143
switch(messageId) {
143144
case 'function-usecallback-props':
144145
case 'object-usememo-props':
146+
case 'jsx-usememo-props':
145147
case 'usememo-const': {
146-
let variableDeclaration = node.type === 'VariableDeclaration' ? node : findParentType(node, 'VariableDeclaration') as TSESTree.VariableDeclaration;
148+
let variableDeclaration = node.type === 'VariableDeclaration' ? node : findParentType(node as Rule.Node, 'VariableDeclaration') as TSESTree.VariableDeclaration;
147149

148150
// Check if it is a hook being stored in let/var, change to const if so
149151
if (variableDeclaration && variableDeclaration.kind !== 'const') {
@@ -157,28 +159,28 @@ export function fixBasedOnMessageId(node: Rule.Node, messageId: keyof typeof Mes
157159
}
158160
}
159161
// If it's an dynamic object - Add useMemo/Callback
160-
if ((isObjExpression || isCorrectableFunctionExpression) ) {
162+
if ((isObjExpression || isJSXElement || isCorrectableFunctionExpression) ) {
161163

162164
const importStatementFixes = addReactImports(context, isCorrectableFunctionExpression ? 'useCallback' : 'useMemo', reactImportData, fixer);
163165
importStatementFixes && fixes.push(importStatementFixes);
164-
const fixed = isCorrectableFunctionExpression ? fixFunction(node as TSESTree.FunctionExpression, context) : `useMemo(() => (${sourceCode.getText(node)}), [])`;
166+
const fixed = isCorrectableFunctionExpression ? fixFunction(node as TSESTree.FunctionExpression, context) : `useMemo(() => (${sourceCode.getText(node as Rule.Node)}), [])`;
165167
const parent = node.parent as unknown as TSESTree.JSXExpressionContainer;
166168
// Means we have a object expression declared directly in jsx
167169
if (parent.type === 'JSXExpressionContainer') {
168170
const parentPropName = (parent?.parent as TSESTree.JSXAttribute)?.name?.name.toString();
169171
const newVarName = getSafeVariableName(context, parentPropName);
170-
const returnStatement = findParentType(node, 'ReturnStatement') as TSESTree.ReturnStatement;
172+
const returnStatement = findParentType(node as Rule.Node, 'ReturnStatement') as TSESTree.ReturnStatement;
171173

172174
if (returnStatement) {
173175
const indentationLevel = sourceCode.lines[returnStatement.loc.start.line - 1].search(/\S/);
174176
const indentation = ' '.repeat(indentationLevel);
175177
// Creates a declaration for the variable and inserts it before the return statement
176178
fixes.push(fixer.insertTextBeforeRange(returnStatement.range,`const ${newVarName} = ${fixed};\n${indentation}`));
177179
// Replaces the old inline object expression with the variable name
178-
fixes.push(fixer.replaceText(node, newVarName));
180+
fixes.push(fixer.replaceText(node as Rule.Node, newVarName));
179181
}
180182
} else {
181-
fixes.push(fixer.replaceText(node, fixed));
183+
fixes.push(fixer.replaceText(node as Rule.Node, fixed));
182184
}
183185

184186
}
@@ -194,7 +196,7 @@ export function fixBasedOnMessageId(node: Rule.Node, messageId: keyof typeof Mes
194196
}
195197

196198
// Simpler cases bellow, all of them are just adding useMemo/Callback
197-
let fixed = `${hook}(() => ${isObjExpression ? "(" : ''}${sourceCode.getText(node as unknown as ESTree.Node)}${isObjExpression ? ")" : ''}, [])`;
199+
let fixed = `${hook}(() => ${isObjExpression || isJSXElement ? "(" : ''}${sourceCode.getText(node as unknown as ESTree.Node)}${isObjExpression ? ")" : ''}, [])`;
198200
const importStatementFixes = addReactImports(context, hook, reactImportData, fixer);
199201
importStatementFixes && fixes.push(importStatementFixes);
200202

@@ -208,7 +210,7 @@ export function fixBasedOnMessageId(node: Rule.Node, messageId: keyof typeof Mes
208210
if ('computed' in node && (node as any)?.computed?.type === 'ArrowFunctionExpression') {
209211
fixes.push(fixer.replaceText((node as any).computed, fixed) as Rule.Fix);
210212
} else {
211-
fixes.push(fixer.replaceText(node, fixed) as Rule.Fix);
213+
fixes.push(fixer.replaceText(node as Rule.Node, fixed) as Rule.Fix);
212214
}
213215
return fixes;
214216
}

0 commit comments

Comments
 (0)