Skip to content

Commit d3dbb2e

Browse files
ZauberNerdfkling
authored andcommitted
Try to resolve static values inside PropType "oneOf"
A common pattern is to write: ```javascript React.PropTypes.oneOf([TYPES.FOO, TYPES.BAR]); // or: var TYPES = ["FOO", "BAR"]; React.PropTypes.oneOf(TYPES); // or: var TYPES = ["A", "B"]; var MORE = [...TYPES, "C"]; React.PropTypes.oneOf(MORE); ``` etc. This change tries to resolve all identifiers (be it the arguments themselves or identifier inside an ArrayExpression) to their value in order to be able to generate better documentation for "oneOf" propTypes that reference static values. This does not work for values that are imported from external files and also does not work for values that are computed (as in `Object.keys(TYPES)` or `_.values(TYPES)`). In those cases it will just print the value with the `computed` flag set to true.
1 parent ec334a2 commit d3dbb2e

File tree

2 files changed

+146
-19
lines changed

2 files changed

+146
-19
lines changed

src/utils/__tests__/getPropType-test.js

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,20 +134,124 @@ describe('getPropType', () => {
134134
});
135135
});
136136

137-
it('resolves variables to their values', () => {
138-
var propTypeExpression = statement(`
139-
PropTypes.shape(shape);
140-
var shape = {bar: PropTypes.string};
141-
`).get('expression');
137+
describe('resolve identifier to their values', () => {
138+
it('resolves variables to their values', () => {
139+
var propTypeExpression = statement(`
140+
PropTypes.shape(shape);
141+
var shape = {bar: PropTypes.string};
142+
`).get('expression');
142143

143-
expect(getPropType(propTypeExpression)).toEqual({
144-
name: 'shape',
145-
value: {
146-
bar: {
147-
name: 'string',
148-
required: false,
144+
expect(getPropType(propTypeExpression)).toEqual({
145+
name: 'shape',
146+
value: {
147+
bar: {
148+
name: 'string',
149+
required: false,
150+
},
149151
},
150-
},
152+
});
153+
});
154+
155+
it('resolves simple identifier to their initialization value', () => {
156+
var propTypeIdentifier = statement(`
157+
PropTypes.oneOf(TYPES);
158+
var TYPES = ["foo", "bar"];
159+
`).get('expression');
160+
161+
expect(getPropType(propTypeIdentifier)).toEqual({
162+
name: 'enum',
163+
value: [
164+
{value: '"foo"', computed: false},
165+
{value: '"bar"', computed: false},
166+
],
167+
});
168+
169+
var identifierInsideArray = statement(`
170+
PropTypes.oneOf([FOO, BAR]);
171+
var FOO = "foo";
172+
var BAR = "bar";
173+
`).get('expression');
174+
175+
expect(getPropType(identifierInsideArray)).toEqual({
176+
name: 'enum',
177+
value: [
178+
{value: '"foo"', computed: false},
179+
{value: '"bar"', computed: false},
180+
],
181+
});
182+
183+
});
184+
185+
it('resolves memberExpressions', () => {
186+
var propTypeExpression = statement(`
187+
PropTypes.oneOf([TYPES.FOO, TYPES.BAR]);
188+
var TYPES = { FOO: "foo", BAR: "bar" };
189+
`).get('expression');
190+
191+
expect(getPropType(propTypeExpression)).toEqual({
192+
name: 'enum',
193+
value: [
194+
{value: '"foo"', computed: false},
195+
{value: '"bar"', computed: false},
196+
],
197+
});
198+
});
199+
200+
it('correctly resolves SpreadElements in arrays', () => {
201+
var propTypeExpression = statement(`
202+
PropTypes.oneOf([...TYPES]);
203+
var TYPES = ["foo", "bar"];
204+
`).get('expression');
205+
206+
expect(getPropType(propTypeExpression)).toEqual({
207+
name: 'enum',
208+
value: [
209+
{value: '"foo"', computed: false},
210+
{value: '"bar"', computed: false},
211+
],
212+
});
213+
});
214+
215+
it('correctly resolves nested SpreadElements in arrays', () => {
216+
var propTypeExpression = statement(`
217+
PropTypes.oneOf([...TYPES]);
218+
var TYPES = ["foo", ...TYPES2];
219+
var TYPES2 = ["bar"];
220+
`).get('expression');
221+
222+
expect(getPropType(propTypeExpression)).toEqual({
223+
name: 'enum',
224+
value: [
225+
{value: '"foo"', computed: false},
226+
{value: '"bar"', computed: false},
227+
],
228+
});
229+
});
230+
231+
it('does not resolve computed values', () => {
232+
var propTypeExpression = statement(`
233+
PropTypes.oneOf(Object.keys(TYPES));
234+
var TYPES = { FOO: "foo", BAR: "bar" };
235+
`).get('expression');
236+
237+
expect(getPropType(propTypeExpression)).toEqual({
238+
name: 'enum',
239+
value: 'Object.keys(TYPES)',
240+
computed: true,
241+
});
242+
});
243+
244+
it('does not resolve external values', () => {
245+
var propTypeExpression = statement(`
246+
PropTypes.oneOf(TYPES);
247+
import { TYPES } from './foo';
248+
`).get('expression');
249+
250+
expect(getPropType(propTypeExpression)).toEqual({
251+
name: 'enum',
252+
value: 'TYPES',
253+
computed: true,
254+
});
151255
});
152256
});
153257

src/utils/getPropType.js

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,44 @@ import resolveToValue from './resolveToValue';
2424
var {types: {namedTypes: types}} = recast;
2525

2626
function getEnumValues(path) {
27-
return path.get('elements').map(function(elementPath) {
28-
return {
29-
value: printValue(elementPath),
30-
computed: !types.Literal.check(elementPath.node),
31-
};
27+
const values = [];
28+
29+
path.get('elements').each(function(elementPath) {
30+
if (types.SpreadElement.check(elementPath.node)) {
31+
const value = resolveToValue(elementPath.get('argument'));
32+
33+
if (types.ArrayExpression.check(value.node)) {
34+
// if the SpreadElement resolved to an Array, add all their elements too
35+
return values.push(...getEnumValues(value));
36+
} else {
37+
// otherwise we'll just print the SpreadElement itself
38+
return values.push({
39+
value: printValue(elementPath),
40+
computed: !types.Literal.check(elementPath.node),
41+
});
42+
}
43+
}
44+
45+
// try to resolve the array element to it's value
46+
const value = resolveToValue(elementPath);
47+
return values.push({
48+
value: printValue(value),
49+
computed: !types.Literal.check(value.node),
50+
});
3251
});
52+
53+
return values;
3354
}
3455

3556
function getPropTypeOneOf(argumentPath) {
3657
var type: PropTypeDescriptor = {name: 'enum'};
37-
if (!types.ArrayExpression.check(argumentPath.node)) {
58+
const value = resolveToValue(argumentPath);
59+
if (!types.ArrayExpression.check(value.node)) {
60+
// could not easily resolve to an Array, let's print the original value
3861
type.computed = true;
3962
type.value = printValue(argumentPath);
4063
} else {
41-
type.value = getEnumValues(argumentPath);
64+
type.value = getEnumValues(value);
4265
}
4366
return type;
4467
}

0 commit comments

Comments
 (0)