Skip to content

Commit 8cef62d

Browse files
committed
Make displayNameHandler take displayName getter into account
Now the display name will correctly be picked up for a component defined as ``` class Component extends React.Component { static get displayName() { return 'Name'; } } ``` Fixes #61
1 parent 1f3cac2 commit 8cef62d

File tree

7 files changed

+135
-14
lines changed

7 files changed

+135
-14
lines changed

src/__tests__/__snapshots__/main-test.js.snap

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,38 @@ Object {
55
"methods": Array []
66
}
77
`;
8+
9+
exports[`main fixtures processes component "component_2.js" without errors 1`] = `
10+
Object {
11+
"description": "",
12+
"displayName": "button",
13+
"methods": Array [
14+
Object {
15+
"docblock": null,
16+
"modifiers": Array [
17+
"static"
18+
],
19+
"name": "displayName",
20+
"params": Array [],
21+
"returns": null
22+
},
23+
Object {
24+
"docblock": null,
25+
"modifiers": Array [
26+
"static"
27+
],
28+
"name": "defaultProps",
29+
"params": Array [],
30+
"returns": null
31+
}
32+
],
33+
"props": Object {
34+
"type": Object {
35+
"defaultValue": Object {
36+
"computed": false,
37+
"value": "\"primary\""
38+
}
39+
}
40+
}
41+
}
42+
`;

src/__tests__/fixtures/component_1.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
/*
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
111
var Component, React;
212

313
React = require('react');

src/__tests__/fixtures/component_2.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
import React from 'react';
12+
13+
export class Button extends React.Component {
14+
static get displayName() {
15+
return "button";
16+
}
17+
18+
static get defaultProps() {
19+
return {
20+
type: "primary",
21+
};
22+
}
23+
}

src/handlers/__tests__/displayNameHandler-test.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,33 @@ describe('defaultPropsHandler', () => {
7070
expect(documentation.displayName).not.toBeDefined();
7171
});
7272

73-
it('ignores non-literal names', () => {
74-
var definition = expression('({displayName: foo.bar})');
75-
expect(() => displayNameHandler(documentation, definition)).not.toThrow();
76-
expect(documentation.displayName).not.toBeDefined();
73+
describe('ClassDeclaration displayName getter', () => {
74+
75+
it('considers class methods', () => {
76+
const definition = statement(`
77+
class Foo {
78+
static get displayName() {
79+
return 'foo';
80+
}
81+
}
82+
`);
83+
expect(() => displayNameHandler(documentation, definition)).not.toThrow();
84+
expect(documentation.displayName).toBe('foo');
85+
});
86+
87+
it('resolves variables in class methods', () => {
88+
const definition = statement(`
89+
class Foo {
90+
static get displayName() {
91+
return abc;
92+
}
93+
}
94+
const abc = 'bar';
95+
`);
96+
expect(() => displayNameHandler(documentation, definition)).not.toThrow();
97+
expect(documentation.displayName).toBe('bar');
98+
});
7799

78-
definition = statement(`
79-
class Foo {
80-
static displayName = foo.bar;
81-
}
82-
`);
83-
expect(() => displayNameHandler(documentation, definition)).not.toThrow();
84-
expect(documentation.displayName).not.toBeDefined();
85100
});
101+
86102
});

src/handlers/displayNameHandler.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,31 @@ import type Documentation from '../Documentation';
1515
import getMemberValuePath from '../utils/getMemberValuePath';
1616
import recast from 'recast';
1717
import resolveToValue from '../utils/resolveToValue';
18+
import {traverseShallow} from '../utils/traverse';
1819

19-
var {types: {namedTypes: types}} = recast;
20+
const {types: {namedTypes: types}} = recast;
2021

2122
export default function displayNameHandler(
2223
documentation: Documentation,
2324
path: NodePath
2425
) {
25-
var displayNamePath = getMemberValuePath(path, 'displayName');
26+
let displayNamePath = getMemberValuePath(path, 'displayName');
2627
if (!displayNamePath) {
2728
return;
2829
}
2930
displayNamePath = resolveToValue(displayNamePath);
31+
32+
// If display name is defined as a getter we get a function expression as
33+
// value. In that case we try to determine the value from the return
34+
// statement.
35+
if (types.FunctionExpression.check(displayNamePath.node)) {
36+
traverseShallow(displayNamePath.get('body'), {
37+
visitReturnStatement: path => {
38+
displayNamePath = resolveToValue(path.get('argument'));
39+
return false;
40+
},
41+
});
42+
}
3043
if (!displayNamePath || !types.Literal.check(displayNamePath.node)) {
3144
return;
3245
}

src/utils/__tests__/getClassMemberValuePath-test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,29 @@ describe('getClassMemberValuePath', () => {
5555
});
5656
});
5757

58+
describe('Getters and Setters', () => {
59+
it('finds getters', () => {
60+
var def = statement(`
61+
class Foo {
62+
get foo() {}
63+
}
64+
`);
65+
66+
expect(getClassMemberValuePath(def, 'foo'))
67+
.toBe(def.get('body', 'body', 0, 'value'));
68+
});
69+
70+
it('ignores setters', () => {
71+
var def = statement(`
72+
class Foo {
73+
set foo(val) {}
74+
}
75+
`);
76+
77+
expect(getClassMemberValuePath(def, 'foo')).not.toBeDefined();
78+
});
79+
});
80+
5881
describe('ClassProperty', () => {
5982
it('finds "normal" class properties', () => {
6083
var def = statement(`

src/utils/getClassMemberValuePath.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export default function getClassMemberValuePath(
2525
return classDefinition.get('body', 'body')
2626
.filter(memberPath => (
2727
(!memberPath.node.computed || types.Literal.check(memberPath.node.key)) &&
28-
getNameOrValue(memberPath.get('key')) === memberName
28+
getNameOrValue(memberPath.get('key')) === memberName &&
29+
memberPath.node.kind !== 'set'
2930
))
3031
.map(memberPath => memberPath.get('value'))[0];
3132
}

0 commit comments

Comments
 (0)