Skip to content

Commit 23aa0fe

Browse files
committed
Support static propTypes getter in classes (fixes #183)
1 parent 3f22b3c commit 23aa0fe

7 files changed

+168
-93
lines changed

src/handlers/__tests__/propDocblockHandler-test.js

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ describe('propDocBlockHandler', () => {
2929

3030
function test(getSrc, parse) {
3131
it('finds docblocks for prop types', () => {
32-
var definition = parse(getSrc(`
33-
{
32+
var definition = parse(getSrc(
33+
`{
3434
/**
3535
* Foo comment
3636
*/
@@ -39,8 +39,8 @@ describe('propDocBlockHandler', () => {
3939
* Bar comment
4040
*/
4141
bar: Prop.bool,
42-
}
43-
`));
42+
}`
43+
));
4444

4545
propDocBlockHandler(documentation, definition);
4646
expect(documentation.descriptors).toEqual({
@@ -54,17 +54,17 @@ describe('propDocBlockHandler', () => {
5454
});
5555

5656
it('can handle multline comments', () => {
57-
var definition = parse(getSrc(`
58-
{
57+
var definition = parse(getSrc(
58+
`{
5959
/**
6060
* Foo comment with
6161
* many lines!
6262
*
6363
* even with empty lines in between
6464
*/
6565
foo: Prop.bool,
66-
}
67-
`));
66+
}`
67+
));
6868

6969
propDocBlockHandler(documentation, definition);
7070
expect(documentation.descriptors).toEqual({
@@ -76,8 +76,8 @@ describe('propDocBlockHandler', () => {
7676
});
7777

7878
it('ignores non-docblock comments', () => {
79-
var definition = parse(getSrc(`
80-
{
79+
var definition = parse(getSrc(
80+
`{
8181
/**
8282
* Foo comment
8383
*/
@@ -88,8 +88,8 @@ describe('propDocBlockHandler', () => {
8888
*/
8989
/* This is not a doc comment */
9090
bar: Prop.bool,
91-
}
92-
`));
91+
}`
92+
));
9393

9494
propDocBlockHandler(documentation, definition);
9595
expect(documentation.descriptors).toEqual({
@@ -103,15 +103,15 @@ describe('propDocBlockHandler', () => {
103103
});
104104

105105
it('only considers the comment with the property below it', () => {
106-
var definition = parse(getSrc(`
107-
{
106+
var definition = parse(getSrc(
107+
`{
108108
/**
109109
* Foo comment
110110
*/
111111
foo: Prop.bool,
112112
bar: Prop.bool,
113-
}
114-
`));
113+
}`
114+
));
115115

116116
propDocBlockHandler(documentation, definition);
117117
expect(documentation.descriptors).toEqual({
@@ -125,15 +125,15 @@ describe('propDocBlockHandler', () => {
125125
});
126126

127127
it('understands and ignores the spread operator', () => {
128-
var definition = parse(getSrc(`
129-
{
128+
var definition = parse(getSrc(
129+
`{
130130
...Foo.propTypes,
131131
/**
132132
* Foo comment
133133
*/
134134
foo: Prop.bool,
135-
}
136-
`));
135+
}`
136+
));
137137

138138
propDocBlockHandler(documentation, definition);
139139
expect(documentation.descriptors).toEqual({
@@ -171,14 +171,29 @@ describe('propDocBlockHandler', () => {
171171
});
172172

173173
describe('ClassDefinition', () => {
174-
test(
175-
propTypesSrc => `
176-
class Foo{
177-
static propTypes = ${propTypesSrc};
178-
}
179-
`,
180-
src => statement(src)
181-
);
174+
describe('class property', () => {
175+
test(
176+
propTypesSrc => `
177+
class Foo{
178+
static propTypes = ${propTypesSrc};
179+
}
180+
`,
181+
src => statement(src)
182+
);
183+
});
184+
185+
describe('static getter', () => {
186+
test(
187+
propTypesSrc => `
188+
class Foo{
189+
static get propTypes() {
190+
return ${propTypesSrc};
191+
}
192+
}
193+
`,
194+
src => statement(src)
195+
);
196+
});
182197
});
183198

184199
it('does not error if propTypes cannot be found', () => {

src/handlers/__tests__/propTypeCompositionHandler-test.js

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ describe('propTypeCompositionHandler', () => {
5050
});
5151

5252
it('understands the spread operator', () => {
53-
var definitionSrc = getSrc(`
54-
{
53+
var definitionSrc = getSrc(
54+
`{
5555
...Foo.propTypes,
5656
...SharedProps,
57-
}
58-
`);
57+
}`
58+
);
5959
var definition = parse(`
6060
${definitionSrc}
6161
var Foo = require("Foo.react");
@@ -76,14 +76,29 @@ describe('propTypeCompositionHandler', () => {
7676
});
7777

7878
describe('class definition', () => {
79-
test(
80-
propTypesSrc => `
81-
class Component {
82-
static propTypes = ${propTypesSrc};
83-
}
84-
`,
85-
src => statement(src)
86-
);
79+
describe('class properties', () => {
80+
test(
81+
propTypesSrc => `
82+
class Component {
83+
static propTypes = ${propTypesSrc};
84+
}
85+
`,
86+
src => statement(src)
87+
);
88+
});
89+
90+
describe('static getter', () => {
91+
test(
92+
propTypesSrc => `
93+
class Component {
94+
static get propTypes() {
95+
return ${propTypesSrc};
96+
}
97+
}
98+
`,
99+
src => statement(src)
100+
);
101+
});
87102
});
88103

89104
it('does not error if propTypes cannot be found', () => {

src/handlers/__tests__/propTypeHandler-test.js

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,11 @@ describe('propTypeHandler', () => {
3737

3838
function test(getSrc, parse) {
3939
it('passes the correct argument to getPropType', () => {
40-
var propTypesSrc = `
41-
{
40+
var propTypesSrc =
41+
`{
4242
foo: PropTypes.bool,
4343
abc: PropTypes.xyz,
44-
}
45-
`;
44+
}`;
4645
var definition = parse(getSrc(propTypesSrc));
4746
var propTypesAST = expression(propTypesSrc);
4847

@@ -58,13 +57,13 @@ describe('propTypeHandler', () => {
5857
});
5958

6059
it('finds definitions via React.PropTypes', () => {
61-
var definition = parse(getSrc(`
62-
{
60+
var definition = parse(getSrc(
61+
`{
6362
foo: PropTypes.bool,
6463
bar: require("react").PropTypes.bool,
6564
baz: OtherPropTypes.bool,
66-
}
67-
`));
65+
}`
66+
));
6867

6968
propTypeHandler(documentation, definition);
7069
expect(documentation.descriptors).toEqual({
@@ -84,11 +83,11 @@ describe('propTypeHandler', () => {
8483
});
8584

8685
it('finds definitions via the ReactPropTypes module', () => {
87-
var definition = parse(getSrc(`
88-
{
86+
var definition = parse(getSrc(
87+
`{
8988
foo: require("ReactPropTypes").bool,
90-
}
91-
`));
89+
}`
90+
));
9291

9392
propTypeHandler(documentation, definition);
9493
expect(documentation.descriptors).toEqual({
@@ -100,13 +99,13 @@ describe('propTypeHandler', () => {
10099
});
101100

102101
it('detects whether a prop is required', () => {
103-
var definition = parse(getSrc(`
104-
{
102+
var definition = parse(getSrc(
103+
`{
105104
simple_prop: PropTypes.array.isRequired,
106105
complex_prop:
107106
PropTypes.oneOfType([PropTypes.number, PropTypes.bool]).isRequired,
108-
}
109-
`));
107+
}`
108+
));
110109

111110
propTypeHandler(documentation, definition);
112111
expect(documentation.descriptors).toEqual({
@@ -122,12 +121,12 @@ describe('propTypeHandler', () => {
122121
});
123122

124123
it('only considers definitions from React or ReactPropTypes', () => {
125-
var definition = parse(getSrc(`
126-
{
124+
var definition = parse(getSrc(
125+
`{
127126
custom_propA: PropTypes.bool,
128127
custom_propB: Prop.bool.isRequired,
129-
}
130-
`));
128+
}`
129+
));
131130

132131
propTypeHandler(documentation, definition);
133132
expect(documentation.descriptors).toEqual({
@@ -171,14 +170,29 @@ describe('propTypeHandler', () => {
171170
});
172171

173172
describe('class definition', () => {
174-
test(
175-
propTypesSrc => template(`
176-
class Component {
177-
static propTypes = ${propTypesSrc};
178-
}
179-
`),
180-
src => statement(src)
181-
);
173+
describe('class property', () => {
174+
test(
175+
propTypesSrc => template(`
176+
class Component {
177+
static propTypes = ${propTypesSrc};
178+
}
179+
`),
180+
src => statement(src)
181+
);
182+
});
183+
184+
describe('static getter', () => {
185+
test(
186+
propTypesSrc => template(`
187+
class Component {
188+
static get propTypes() {
189+
return ${propTypesSrc};
190+
}
191+
}
192+
`),
193+
src => statement(src)
194+
);
195+
});
182196
});
183197

184198
describe('stateless component', () => {

src/handlers/defaultPropsHandler.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import getMemberValuePath from '../utils/getMemberValuePath';
1717
import printValue from '../utils/printValue';
1818
import recast from 'recast';
1919
import resolveToValue from '../utils/resolveToValue';
20+
import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue';
2021
import isStatelessComponent from '../utils/isStatelessComponent';
2122

22-
var {types: {namedTypes: types, visit}} = recast;
23+
var {types: {namedTypes: types}} = recast;
2324

2425
function getDefaultValue(path: NodePath) {
2526
var node = path.node;
@@ -70,16 +71,10 @@ function getDefaultPropsPath(componentDefinition: NodePath): ?NodePath {
7071
if (types.FunctionExpression.check(defaultPropsPath.node)) {
7172
// Find the value that is returned from the function and process it if it is
7273
// an object literal.
73-
visit(defaultPropsPath.get('body'), {
74-
visitFunction: () => false,
75-
visitReturnStatement: function(path) {
76-
var resolvedPath = resolveToValue(path.get('argument'));
77-
if (types.ObjectExpression.check(resolvedPath.node)) {
78-
defaultPropsPath = resolvedPath;
79-
}
80-
return false;
81-
},
82-
});
74+
const returnValue = resolveFunctionDefinitionToReturnValue(defaultPropsPath);
75+
if (returnValue && types.ObjectExpression.check(returnValue.node)) {
76+
defaultPropsPath = returnValue;
77+
}
8378
}
8479
return defaultPropsPath;
8580
}

src/handlers/displayNameHandler.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import getMemberValuePath from '../utils/getMemberValuePath';
1616
import getNameOrValue from '../utils/getNameOrValue';
1717
import recast from 'recast';
1818
import resolveToValue from '../utils/resolveToValue';
19-
import {traverseShallow} from '../utils/traverse';
19+
import resolveFunctionDefinitionToReturnValue from '../utils/resolveFunctionDefinitionToReturnValue';
2020

2121
const {types: {namedTypes: types}} = recast;
2222

@@ -42,12 +42,7 @@ export default function displayNameHandler(
4242
// value. In that case we try to determine the value from the return
4343
// statement.
4444
if (types.FunctionExpression.check(displayNamePath.node)) {
45-
traverseShallow(displayNamePath.get('body'), {
46-
visitReturnStatement: path => {
47-
displayNamePath = resolveToValue(path.get('argument'));
48-
return false;
49-
},
50-
});
45+
displayNamePath = resolveFunctionDefinitionToReturnValue(displayNamePath);
5146
}
5247
if (!displayNamePath || !types.Literal.check(displayNamePath.node)) {
5348
return;

0 commit comments

Comments
 (0)