Skip to content

Commit ea9cbeb

Browse files
mfazekasdanez
andauthored
fix: Fix wrong detection of forwardRef in combination with memo (#592)
Release-As: 6.0.0-alpha.3 Co-authored-by: Daniel Tschinder <[email protected]>
1 parent c78e9bd commit ea9cbeb

File tree

7 files changed

+55
-11
lines changed

7 files changed

+55
-11
lines changed

src/__tests__/__snapshots__/main-test.ts.snap

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,22 @@ Object {
18191819
}
18201820
`;
18211821
1822+
exports[`main fixtures processes component "component_42.js" without errors 1`] = `
1823+
Object {
1824+
"description": "",
1825+
"methods": Array [],
1826+
"props": Object {
1827+
"foo": Object {
1828+
"defaultValue": Object {
1829+
"computed": false,
1830+
"value": "'bar'",
1831+
},
1832+
"required": false,
1833+
},
1834+
},
1835+
}
1836+
`;
1837+
18221838
exports[`main fixtures processes component "flow-export-type.js" without errors 1`] = `
18231839
Object {
18241840
"description": "This is a Flow class component",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import React, {memo, forwardRef} from 'react';
2+
3+
export default memo(forwardRef(({ foo = 'bar' }, ref) => <div ref={ref}>{foo}</div>));

src/handlers/__tests__/componentDocblockHandler-test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,22 @@ describe('componentDocblockHandler', () => {
320320
);
321321
});
322322

323+
describe('inline implementation with memo', () => {
324+
test(`
325+
React.memo(React.forwardRef((props, ref) => {}));
326+
import React from "react";`, src =>
327+
beforeLastStatement(src).get('expression'));
328+
329+
testImports(
330+
`
331+
export default React.memo(React.forwardRef((props, ref) => {}));
332+
import React from 'react';`,
333+
src => beforeLastStatement(src).get('declaration'),
334+
'RefComponent',
335+
useDefault,
336+
);
337+
});
338+
323339
describe('out of line implementation', () => {
324340
test(`let Component = (props, ref) => {};
325341
React.forwardRef(Component);

src/handlers/defaultPropsHandler.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ function getStatelessPropsPath(
4646
componentDefinition: NodePath,
4747
importer: Importer,
4848
): NodePath {
49-
const value = resolveToValue(componentDefinition, importer);
49+
let value = resolveToValue(componentDefinition, importer);
50+
5051
if (isReactForwardRefCall(value, importer)) {
51-
const inner = resolveToValue(value.get('arguments', 0), importer);
52-
return inner.get('params', 0);
52+
value = resolveToValue(value.get('arguments', 0), importer);
5353
}
54+
5455
return value.get('params', 0);
5556
}
5657

src/utils/__tests__/isReactForwardRefCall-test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ describe('isReactForwardRefCall', () => {
7777
expect(isReactForwardRefCall(def, noopImporter)).toBe(true);
7878
});
7979

80+
it('does not accept forwardRef if not outer call', () => {
81+
const def = expressionLast(`
82+
import { forwardRef, memo } from "react";
83+
memo(forwardRef({}));
84+
`);
85+
expect(isReactForwardRefCall(def, noopImporter)).toBe(false);
86+
});
87+
8088
it('accepts forwardRef called on imported aliased value', () => {
8189
const def = expressionLast(`
8290
import { forwardRef as foo } from "react";

src/utils/isReactBuiltinCall.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@ export default function isReactBuiltinCall(
3131

3232
// Check if this is a destructuring assignment
3333
// const { x } = require('react')
34-
if (isDestructuringAssignment(value, name)) {
35-
const module = resolveToModule(value, importer);
36-
return Boolean(module && isReactModuleName(module));
37-
} else if (
34+
if (
35+
isDestructuringAssignment(value, name) ||
3836
// `require('react').createElement`
3937
(t.MemberExpression.check(value.node) &&
4038
t.Identifier.check(value.get('property').node) &&
@@ -43,11 +41,14 @@ export default function isReactBuiltinCall(
4341
(t.ImportDeclaration.check(value.node) &&
4442
value.node.specifiers &&
4543
value.node.specifiers.some(
46-
// @ts-ignore
47-
specifier => specifier.imported && specifier.imported.name === name,
44+
specifier =>
45+
// @ts-ignore
46+
specifier.imported?.name === name &&
47+
specifier.local?.name === path.node.callee.name,
4848
))
4949
) {
5050
const module = resolveToModule(value, importer);
51+
5152
return Boolean(module && isReactModuleName(module));
5253
}
5354
}

src/utils/isReactCreateClassCall.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { namedTypes as t } from 'ast-types';
2-
import match from './match';
32
import resolveToModule from './resolveToModule';
43
import isReactBuiltinCall from './isReactBuiltinCall';
54
import type { Importer } from '../parse';
@@ -20,7 +19,7 @@ function isReactCreateClassCallModular(
2019
path = path.get('expression');
2120
}
2221

23-
if (!match(path.node, { type: 'CallExpression' })) {
22+
if (!t.CallExpression.check(path.node)) {
2423
return false;
2524
}
2625
const module = resolveToModule(path, importer);

0 commit comments

Comments
 (0)