Skip to content

Commit 328628f

Browse files
committed
add support for components wrapped in forwardRef
1 parent 7e15965 commit 328628f

File tree

3 files changed

+125
-7
lines changed

3 files changed

+125
-7
lines changed

.vscode/launch.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Debug Jest Tests",
6+
"type": "node",
7+
"request": "launch",
8+
"runtimeArgs": [
9+
"--inspect-brk",
10+
"${workspaceRoot}/node_modules/.bin/jest",
11+
"--runInBand"
12+
],
13+
"console": "integratedTerminal",
14+
"internalConsoleOptions": "neverOpen"
15+
}
16+
]
17+
}

index.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,29 @@ function getReturnStatement(node) {
4848
);
4949
}
5050

51+
if (node.type === 'ArrowFunctionExpression') return node.body;
5152
return node.type === 'VariableDeclaration'
5253
? node.declarations?.[0]?.init?.body?.body?.find(
5354
(statement) => statement.type === 'ReturnStatement',
54-
) ?? node.declarations?.[0]?.init?.body
55+
) ??
56+
node.declarations?.[0]?.init?.arguments?.[0]?.body ??
57+
node.declarations?.[0]?.init?.body
5558
: node.body?.body?.find(
5659
(statement) => statement.type === 'ReturnStatement',
5760
);
5861
}
5962

63+
function isForwardRef(node) {
64+
if (!Boolean(node)) {
65+
return;
66+
}
67+
68+
return node.type === 'VariableDeclaration'
69+
? node.declarations?.[0]?.init?.callee?.property?.name === 'forwardRef'
70+
: node.callee?.name === 'forwardRef' ||
71+
node.callee?.property?.name === 'forwardRef';
72+
}
73+
6074
function isTreeDone(node, excludeComponentNames) {
6175
return (
6276
node.type === 'JSXElement' &&
@@ -107,12 +121,21 @@ const rules = {
107121
return {
108122
Program(node) {
109123
const componentNodes = node.body
110-
.map((child) => child?.declaration ?? child)
124+
.map((child) => {
125+
const declaration = child?.declaration ?? child;
126+
if (isForwardRef(declaration)) {
127+
// do something
128+
return declaration?.arguments?.[0] ?? declaration;
129+
}
130+
return declaration;
131+
})
111132
.filter(
112133
(child) =>
113134
child.type === 'VariableDeclaration' ||
114135
child.type === 'FunctionDeclaration' ||
115-
child.type === 'ClassDeclaration',
136+
child.type === 'ClassDeclaration' ||
137+
child.type === 'FunctionExpression' ||
138+
child.type === 'ArrowFunctionExpression',
116139
)
117140
.filter((child) => {
118141
let flag = false;

test.js

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ const classComponentNested = `class Car extends React.Component {
132132
const reactForwardRef = /* tsx */ `
133133
export const InternalLink = React.forwardRef<HTMLAnchorElement, InternalLinkProps>(
134134
({ variant, ...props }, ref) => (
135-
<Link
136-
data-component="InternalLink"
135+
<Link data-component="InternalLink"
137136
ref={ref}
138137
className={classNames(css.link, { [css.inverse]: variant === 'inverse' })}
139138
{...props}
@@ -159,8 +158,7 @@ export const InternalLink = React.forwardRef<HTMLAnchorElement, InternalLinkProp
159158
const forwardRef = /* tsx */ `
160159
export const InternalLink = forwardRef<HTMLAnchorElement, InternalLinkProps>(
161160
({ variant, ...props }, ref) => (
162-
<Link
163-
data-component="InternalLink"
161+
<Link data-component="InternalLink"
164162
ref={ref}
165163
className={classNames(css.link, { [css.inverse]: variant === 'inverse' })}
166164
{...props}
@@ -183,6 +181,66 @@ export const InternalLink = forwardRef<HTMLAnchorElement, InternalLinkProps>(
183181
),
184182
);`;
185183

184+
const defaultReactForwardRef = /* tsx */ `
185+
export default React.forwardRef<HTMLAnchorElement, InternalLinkProps>(
186+
function InternalLink({ variant, ...props }, ref) {
187+
return (
188+
<Link data-component="InternalLink"
189+
ref={ref}
190+
className={classNames(css.link, { [css.inverse]: variant === 'inverse' })}
191+
{...props}
192+
>
193+
{props.children}
194+
</Link>
195+
);
196+
}
197+
);`;
198+
199+
const defaultReactForwardRefError = /* tsx */ `
200+
export default React.forwardRef<HTMLAnchorElement, InternalLinkProps>(
201+
function InternalLink({ variant, ...props }, ref) {
202+
return (
203+
<Link
204+
ref={ref}
205+
className={classNames(css.link, { [css.inverse]: variant === 'inverse' })}
206+
{...props}
207+
>
208+
{props.children}
209+
</Link>
210+
);
211+
}
212+
);`;
213+
214+
const defaultForwardRef = /* tsx */ `
215+
export default forwardRef<HTMLAnchorElement, InternalLinkProps>(
216+
function InternalLink({ variant, ...props }, ref) {
217+
return (
218+
<Link data-component="InternalLink"
219+
ref={ref}
220+
className={classNames(css.link, { [css.inverse]: variant === 'inverse' })}
221+
{...props}
222+
>
223+
{props.children}
224+
</Link>
225+
);
226+
}
227+
);`;
228+
229+
const defaultForwardRefError = /* tsx */ `
230+
export default forwardRef<HTMLAnchorElement, InternalLinkProps>(
231+
function InternalLink({ variant, ...props }, ref) {
232+
return (
233+
<Link
234+
ref={ref}
235+
className={classNames(css.link, { [css.inverse]: variant === 'inverse' })}
236+
{...props}
237+
>
238+
{props.children}
239+
</Link>
240+
);
241+
}
242+
);`;
243+
186244
const tests = {
187245
'data-component': {
188246
// Require the actual rule definition
@@ -234,6 +292,12 @@ const tests = {
234292
{
235293
code: forwardRef,
236294
},
295+
{
296+
code: defaultReactForwardRef,
297+
},
298+
{
299+
code: defaultForwardRef,
300+
},
237301
],
238302
invalid: [
239303
{
@@ -294,6 +358,20 @@ const tests = {
294358
'InternalLink is missing the data-component attribute for the top-level element.',
295359
],
296360
},
361+
{
362+
code: defaultReactForwardRefError,
363+
output: defaultReactForwardRef,
364+
errors: [
365+
'InternalLink is missing the data-component attribute for the top-level element.',
366+
],
367+
},
368+
{
369+
code: defaultForwardRefError,
370+
output: defaultForwardRef,
371+
errors: [
372+
'InternalLink is missing the data-component attribute for the top-level element.',
373+
],
374+
},
297375
],
298376
},
299377
},

0 commit comments

Comments
 (0)