Skip to content

Commit 3889316

Browse files
committed
Remove handling of ExpressionStatments and add type hints for TypeScript
`__reactBuiltinTypeHint` is added so that Typescript does not think it is just a CallExpression, because in reality we check for way more than if it is just a CallExpression. Adding that hint makes TypeScript realize that even though the `isSomething()` helper returned false it might still be a CallExpression.
1 parent 138dec6 commit 3889316

13 files changed

+33
-35
lines changed

Diff for: packages/react-docgen/src/handlers/componentDocblockHandler.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function getDocblockFromComponent(path: NodePath): string | null {
3939
}
4040
if (!description) {
4141
const searchPath = isReactForwardRefCall(path)
42-
? path.get('arguments')[0]
42+
? path.get('arguments')[0]!
4343
: path;
4444
const inner = resolveToValue(searchPath);
4545

Diff for: packages/react-docgen/src/handlers/componentMethodsHandler.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function findStatelessComponentBody(
141141
return body;
142142
}
143143
} else if (isReactForwardRefCall(componentDefinition)) {
144-
const inner = resolveToValue(componentDefinition.get('arguments')[0]);
144+
const inner = resolveToValue(componentDefinition.get('arguments')[0]!);
145145

146146
return findStatelessComponentBody(inner);
147147
}

Diff for: packages/react-docgen/src/handlers/defaultPropsHandler.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function getStatelessPropsPath(
6060
let value = resolveToValue(componentDefinition);
6161

6262
if (isReactForwardRefCall(value)) {
63-
value = resolveToValue(value.get('arguments')[0]);
63+
value = resolveToValue(value.get('arguments')[0]!);
6464
}
6565

6666
return value.get('params')[0];

Diff for: packages/react-docgen/src/utils/__tests__/isExportsOrModuleAssignment-test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ import { describe, expect, test } from 'vitest';
55
describe('isExportsOrModuleAssignment', () => {
66
test('detects "module.exports = ...;"', () => {
77
expect(
8-
isExportsOrModuleAssignment(parse.statement('module.exports = foo;')),
8+
isExportsOrModuleAssignment(parse.expression('module.exports = foo')),
99
).toBe(true);
1010
});
1111

1212
test('detects "exports.foo = ..."', () => {
1313
expect(
14-
isExportsOrModuleAssignment(parse.statement('exports.foo = foo;')),
14+
isExportsOrModuleAssignment(parse.expression('exports.foo = foo')),
1515
).toBe(true);
1616
});
1717

1818
test('does not accept "exports = foo;"', () => {
1919
// That doesn't actually export anything
20-
expect(isExportsOrModuleAssignment(parse.statement('exports = foo;'))).toBe(
20+
expect(isExportsOrModuleAssignment(parse.expression('exports = foo'))).toBe(
2121
false,
2222
);
2323
});

Diff for: packages/react-docgen/src/utils/findComponentDefinition.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function resolveComponentDefinition(
2424
): NodePath<ComponentNode> | null {
2525
if (isReactCreateClassCall(definition)) {
2626
// return argument
27-
const resolvedPath = resolveToValue(definition.get('arguments')[0]);
27+
const resolvedPath = resolveToValue(definition.get('arguments')[0]!);
2828

2929
if (resolvedPath.isObjectExpression()) {
3030
return resolvedPath;

Diff for: packages/react-docgen/src/utils/getTypeFromReactComponent.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function getStatelessPropsPath(
3030
let value = resolveToValue(componentDefinition);
3131

3232
if (isReactForwardRefCall(value)) {
33-
value = resolveToValue(value.get('arguments')[0]);
33+
value = resolveToValue(value.get('arguments')[0]!);
3434
}
3535

3636
if (!value.isFunction()) return;

Diff for: packages/react-docgen/src/utils/isExportsOrModuleAssignment.ts

-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ import * as expressionTo from './expressionTo.js';
66
* `modules.exports = ...;`.
77
*/
88
export default function isExportsOrModuleAssignment(path: NodePath): boolean {
9-
if (path.isExpressionStatement()) {
10-
path = path.get('expression');
11-
}
129
if (
1310
!path.isAssignmentExpression() ||
1411
!path.get('left').isMemberExpression()

Diff for: packages/react-docgen/src/utils/isReactBuiltinCall.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ function isNamedImportDeclaration(
4646
export default function isReactBuiltinCall(
4747
path: NodePath,
4848
name: string,
49-
): boolean {
50-
if (path.isExpressionStatement()) {
51-
path = path.get('expression');
52-
}
53-
49+
): path is NodePath<CallExpression & { __reactBuiltinTypeHint: true }> {
5450
if (path.isCallExpression()) {
5551
const callee = path.get('callee');
5652

Diff for: packages/react-docgen/src/utils/isReactChildrenElementCall.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import type { NodePath } from '@babel/traverse';
22
import isReactModuleName from './isReactModuleName.js';
33
import resolveToModule from './resolveToModule.js';
4+
import { CallExpression } from '@babel/types';
45

56
/**
67
* Returns true if the expression is a function call of the form
78
* `React.Children.only(...)` or `React.Children.map(...)`.
89
*/
9-
export default function isReactChildrenElementCall(path: NodePath): boolean {
10-
if (path.isExpressionStatement()) {
11-
path = path.get('expression');
12-
}
13-
10+
export default function isReactChildrenElementCall(
11+
path: NodePath,
12+
): path is NodePath<CallExpression & { __reactBuiltinTypeHint: true }> {
1413
if (!path.isCallExpression()) {
1514
return false;
1615
}
+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { NodePath } from '@babel/traverse';
2-
import type { Expression, ExpressionStatement } from '@babel/types';
2+
import type { CallExpression } from '@babel/types';
33
import isReactBuiltinCall from './isReactBuiltinCall.js';
44

55
/**
66
* Returns true if the expression is a function call of the form
77
* `React.cloneElement(...)`.
88
*/
99
export default function isReactCloneElementCall(
10-
path: NodePath<Expression | ExpressionStatement>,
11-
): boolean {
10+
path: NodePath,
11+
): path is NodePath<CallExpression & { __reactBuiltinTypeHint: true }> {
1212
return isReactBuiltinCall(path, 'cloneElement');
1313
}

Diff for: packages/react-docgen/src/utils/isReactCreateClassCall.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { NodePath } from '@babel/traverse';
22
import resolveToModule from './resolveToModule.js';
33
import isReactBuiltinCall from './isReactBuiltinCall.js';
4+
import { CallExpression } from '@babel/types';
45

56
/**
67
* Returns true if the expression is a function call of the form
@@ -10,10 +11,6 @@ import isReactBuiltinCall from './isReactBuiltinCall.js';
1011
* ```
1112
*/
1213
function isReactCreateClassCallModular(path: NodePath): boolean {
13-
if (path.isExpressionStatement()) {
14-
path = path.get('expression');
15-
}
16-
1714
if (!path.isCallExpression()) {
1815
return false;
1916
}
@@ -30,9 +27,12 @@ function isReactCreateClassCallModular(path: NodePath): boolean {
3027
* createReactClass(...);
3128
* ```
3229
*/
33-
export default function isReactCreateClassCall(path: NodePath): boolean {
30+
export default function isReactCreateClassCall(
31+
path: NodePath,
32+
): path is NodePath<CallExpression & { __reactBuiltinTypeHint: true }> {
3433
return (
35-
isReactBuiltinCall(path, 'createClass') ||
34+
(isReactBuiltinCall(path, 'createClass') &&
35+
path.get('arguments').length === 1) ||
3636
isReactCreateClassCallModular(path)
3737
);
3838
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { NodePath } from '@babel/traverse';
2-
import type { Expression, ExpressionStatement } from '@babel/types';
2+
import type { CallExpression } from '@babel/types';
33
import isReactBuiltinCall from './isReactBuiltinCall.js';
44

55
/**
66
* Returns true if the expression is a function call of the form
77
* `React.createElement(...)`.
88
*/
99
export default function isReactCreateElementCall(
10-
path: NodePath<Expression | ExpressionStatement>,
11-
): boolean {
10+
path: NodePath,
11+
): path is NodePath<CallExpression & { __reactBuiltinTypeHint: true }> {
1212
return isReactBuiltinCall(path, 'createElement');
1313
}
+8-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import type { NodePath } from '@babel/traverse';
22
import isReactBuiltinCall from './isReactBuiltinCall.js';
3+
import { CallExpression } from '@babel/types';
34

45
/**
56
* Returns true if the expression is a function call of the form
67
* `React.forwardRef(...)`.
78
*/
8-
export default function isReactForwardRefCall(path: NodePath): boolean {
9-
return isReactBuiltinCall(path, 'forwardRef');
9+
export default function isReactForwardRefCall(
10+
path: NodePath,
11+
): path is NodePath<CallExpression & { __reactBuiltinTypeHint: true }> {
12+
return (
13+
isReactBuiltinCall(path, 'forwardRef') &&
14+
(path as NodePath<CallExpression>).get('arguments').length === 1
15+
);
1016
}

0 commit comments

Comments
 (0)