• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

reactjs / react-docgen / 15503494088

07 Jun 2025 02:52AM CUT coverage: 95.479% (-0.01%) from 95.49%
15503494088

Pull #999

github

web-flow
Merge c42d1b242 into ec11d1f1e
Pull Request #999: Packages ready to publish

1393 of 1494 branches covered (93.24%)

Branch coverage included in aggregate %.

4605 of 4788 relevant lines covered (96.18%)

522.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.72
/packages/react-docgen/src/utils/getMethodDocumentation.ts
1
import type { NodePath } from '@babel/traverse';
2
import type {
3
  AssignmentExpression,
4
  ClassMethod,
5
  ClassPrivateMethod,
6
  ClassProperty,
7
  Function as FunctionType,
8
  ObjectMethod,
9
  ObjectProperty,
10
} from '@babel/types';
11
import { getDocblock } from './docblock.js';
3✔
12
import getFlowType from './getFlowType.js';
3✔
13
import getTSType from './getTSType.js';
3✔
14
import getParameterName from './getParameterName.js';
3✔
15
import getPropertyName from './getPropertyName.js';
3✔
16
import getTypeAnnotation from './getTypeAnnotation.js';
3✔
17
import resolveToValue from './resolveToValue.js';
3✔
18
import printValue from './printValue.js';
3✔
19
import type {
20
  MethodDescriptor,
21
  MethodModifier,
22
  MethodParameter,
23
  MethodReturn,
24
  TypeDescriptor,
25
} from '../Documentation.js';
26

27
export type MethodNodePath =
28
  | NodePath<AssignmentExpression>
29
  | NodePath<ClassMethod>
30
  | NodePath<ClassPrivateMethod>
31
  | NodePath<ClassProperty>
32
  | NodePath<ObjectMethod>
33
  | NodePath<ObjectProperty>;
34

35
function getMethodFunctionExpression(
585✔
36
  methodPath: MethodNodePath,
585✔
37
): NodePath<FunctionType> | null {
585✔
38
  if (methodPath.isClassMethod() || methodPath.isObjectMethod()) {
585✔
39
    return methodPath;
279✔
40
  }
279✔
41

42
  const potentialFunctionExpression = methodPath.isAssignmentExpression()
306✔
43
    ? methodPath.get('right')
72✔
44
    : (methodPath.get('value') as NodePath);
234✔
45

46
  const functionExpression = resolveToValue(potentialFunctionExpression);
585✔
47

48
  if (functionExpression.isFunction()) {
585✔
49
    return functionExpression;
306✔
50
  }
306!
51

52
  return null;
×
53
}
×
54

55
function getMethodParamOptional(
63✔
56
  path: NodePath<FunctionType['params'][number]>,
63✔
57
): boolean {
63✔
58
  let identifier: NodePath = path;
63✔
59

60
  if (identifier.isTSParameterProperty()) {
63!
61
    identifier = identifier.get('parameter');
×
62
  }
×
63
  if (identifier.isAssignmentPattern()) {
63!
64
    // A default value always makes the param optional
65
    return true;
×
66
  }
×
67

68
  return identifier.isIdentifier() ? Boolean(identifier.node.optional) : false;
63✔
69
}
63✔
70

71
function getMethodParamsDoc(methodPath: MethodNodePath): MethodParameter[] {
207✔
72
  const params: MethodParameter[] = [];
207✔
73
  const functionExpression = getMethodFunctionExpression(methodPath);
207✔
74

75
  if (functionExpression) {
207✔
76
    // Extract param types.
77
    functionExpression.get('params').forEach((paramPath) => {
207✔
78
      let type: TypeDescriptor | null = null;
63✔
79
      const typePath = getTypeAnnotation(paramPath);
63✔
80

81
      if (typePath) {
63✔
82
        if (typePath.isFlowType()) {
60✔
83
          type = getFlowType(typePath, null);
48✔
84
          if (typePath.isGenericTypeAnnotation()) {
48✔
85
            type.alias = printValue(typePath.get('id'));
3✔
86
          }
3✔
87
        } else if (typePath.isTSType()) {
60✔
88
          type = getTSType(typePath, null);
12✔
89
          if (typePath.isTSTypeReference()) {
12✔
90
            type.alias = printValue(typePath.get('typeName'));
3✔
91
          }
3✔
92
        }
12✔
93
      }
60✔
94

95
      const param = {
63✔
96
        name: getParameterName(paramPath),
63✔
97
        optional: getMethodParamOptional(paramPath),
63✔
98
        type,
63✔
99
      };
63✔
100

101
      params.push(param);
63✔
102
    });
207✔
103
  }
207✔
104

105
  return params;
207✔
106
}
207✔
107

108
// Extract flow return type.
109
function getMethodReturnDoc(methodPath: MethodNodePath): MethodReturn | null {
207✔
110
  const functionExpression = getMethodFunctionExpression(methodPath);
207✔
111

112
  if (functionExpression && functionExpression.node.returnType) {
207✔
113
    const returnType = getTypeAnnotation(functionExpression.get('returnType'));
51✔
114

115
    if (!returnType) {
51!
116
      return null;
×
117
    }
×
118

119
    if (returnType.isFlowType()) {
51✔
120
      return { type: getFlowType(returnType, null) };
45✔
121
    } else if (returnType.isTSType()) {
45✔
122
      return { type: getTSType(returnType, null) };
6✔
123
    }
6✔
124
  }
51✔
125

126
  return null;
156✔
127
}
156✔
128

129
function getMethodModifiers(
207✔
130
  methodPath: MethodNodePath,
207✔
131
  options: { isStatic?: boolean },
207✔
132
): MethodModifier[] {
207✔
133
  if (methodPath.isAssignmentExpression()) {
207✔
134
    return ['static'];
36✔
135
  }
36✔
136

137
  // Otherwise this is a method/property node
138

139
  const modifiers: MethodModifier[] = [];
171✔
140

141
  if (
171✔
142
    options.isStatic === true ||
171✔
143
    ((methodPath.isClassProperty() || methodPath.isClassMethod()) &&
162✔
144
      methodPath.node.static)
117✔
145
  ) {
207✔
146
    modifiers.push('static');
27✔
147
  }
27✔
148

149
  const functionExpression = getMethodFunctionExpression(methodPath);
171✔
150

151
  if (functionExpression) {
171✔
152
    if (
171✔
153
      functionExpression.isClassMethod() ||
171✔
154
      functionExpression.isObjectMethod()
93✔
155
    ) {
171✔
156
      if (
93✔
157
        functionExpression.node.kind === 'get' ||
93✔
158
        functionExpression.node.kind === 'set'
87✔
159
      ) {
93✔
160
        modifiers.push(functionExpression.node.kind);
6✔
161
      }
6✔
162
    }
93✔
163

164
    if (functionExpression.node.generator) {
171✔
165
      modifiers.push('generator');
3✔
166
    }
3✔
167
    if (functionExpression.node.async) {
171✔
168
      modifiers.push('async');
6✔
169
    }
6✔
170
  }
171✔
171

172
  return modifiers;
171✔
173
}
171✔
174

175
function getMethodName(
216✔
176
  methodPath: Exclude<MethodNodePath, NodePath<ClassPrivateMethod>>,
216✔
177
): string | null {
216✔
178
  if (methodPath.isAssignmentExpression()) {
216✔
179
    const left = methodPath.get('left');
36✔
180

181
    if (left.isMemberExpression()) {
36✔
182
      const property = left.get('property');
36✔
183

184
      if (!left.node.computed && property.isIdentifier()) {
36✔
185
        return property.node.name;
36✔
186
      }
36!
187
      if (property.isStringLiteral() || property.isNumericLiteral()) {
36!
188
        return String(property.node.value);
×
189
      }
×
190
    }
36!
191

192
    return null;
×
193
  }
✔
194

195
  return getPropertyName(methodPath);
180✔
196
}
180✔
197

198
function getMethodAccessibility(
225✔
199
  methodPath: MethodNodePath,
225✔
200
): 'private' | 'protected' | 'public' | null {
225✔
201
  if (methodPath.isClassMethod() || methodPath.isClassProperty()) {
225✔
202
    return methodPath.node.accessibility || null;
132✔
203
  }
132✔
204

205
  // Otherwise this is a object method/property or assignment expression
206
  return null;
93✔
207
}
93✔
208

209
function getMethodDocblock(methodPath: MethodNodePath): string | null {
207✔
210
  if (methodPath.isAssignmentExpression()) {
207✔
211
    let path: NodePath | null = methodPath;
36✔
212

213
    do {
36✔
214
      path = path.parentPath;
36✔
215
    } while (path && !path.isExpressionStatement());
36✔
216

217
    if (path) {
36✔
218
      return getDocblock(path);
36✔
219
    }
36!
220

221
    return null;
×
222
  }
✔
223

224
  // Otherwise this is a method/property node
225
  return getDocblock(methodPath);
171✔
226
}
171✔
227

228
// Gets the documentation object for a component method.
229
// Component methods may be represented as class/object method/property nodes
230
// or as assignment expression of the form `Component.foo = function() {}`
231
export default function getMethodDocumentation(
225✔
232
  methodPath: MethodNodePath,
225✔
233
  options: { isStatic?: boolean } = {},
225✔
234
): MethodDescriptor | null {
225✔
235
  if (
225✔
236
    getMethodAccessibility(methodPath) === 'private' ||
225✔
237
    methodPath.isClassPrivateMethod()
219✔
238
  ) {
225✔
239
    return null;
9✔
240
  }
9✔
241

242
  const name = getMethodName(methodPath);
216✔
243

244
  if (!name) return null;
225✔
245

246
  return {
207✔
247
    name,
207✔
248
    docblock: getMethodDocblock(methodPath),
207✔
249
    modifiers: getMethodModifiers(methodPath, options),
207✔
250
    params: getMethodParamsDoc(methodPath),
207✔
251
    returns: getMethodReturnDoc(methodPath),
207✔
252
  };
207✔
253
}
207✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc