Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
},
"globals": {
"ASTNode": true,
"FlowTypeDescriptor": true,
"Handler": true,
"NodePath": true,
"PropTypeDescriptor": true,
"PropDescriptor": true,
"PropTypeDescriptor": true,
"Resolver": true
}
}
1 change: 1 addition & 0 deletions flow/recast.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ declare class NodePath {
each(f: (p: NodePath) => any): any;
map<T>(f: (p: NodePath) => T): Array<T>;
filter(f: (p: NodePath) => bool): Array<NodePath>;
push(node: ASTNode): void;
}

type Recast = {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"async": "^1.4.2",
"babel-runtime": "~5.8.25",
"babylon": "~5.8.3",
"doctrine": "^1.2.0",
"node-dir": "^0.1.10",
"nomnom": "^1.8.1",
"recast": "^0.10.41"
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/main-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('main', () => {
expect(docs).toEqual({
displayName: 'ABC',
description: 'Example component description',
methods: [],
props: {
foo: {
type: {
Expand Down
126 changes: 126 additions & 0 deletions src/handlers/__tests__/componentMethodsHandler-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/

/*global jest, describe, beforeEach, it, expect*/

jest.autoMockOff();
jest.mock('../../Documentation');

describe('componentMethodsHandler', () => {
let documentation;
let componentMethodsHandler;
let parse;

beforeEach(() => {
({parse} = require('../../../tests/utils'));
documentation = new (require('../../Documentation'));
componentMethodsHandler = require('../componentMethodsHandler');
});

function test(definition) {
componentMethodsHandler(documentation, definition);
expect(documentation.methods).toEqual([{
name: 'foo',
description: 'The foo method',
visibility: 'protected',
modifiers: [],
return: {
description: 'The number',
type: {name: 'number'},
},
params: [{
name: 'bar',
description: 'The bar param',
type: {name: 'number'},
}],
}, {
name: 'bar',
description: 'Static function',
visibility: 'public',
modifiers: ['static'],
return: null,
params: [],
}]);
}

it('extracts the documentation for an ObjectExpression', () => {
const src = `
({
/**
* The foo method
* @protected
* @param bar The bar param
* @returns The number
*/
foo(bar: number): number {
return bar;
},
statics: {
/**
* Static function
*/
bar() {}
},
state: {
foo: 'foo',
},
componentDidMount() {},
render() {
return null;
},
})
`;

test(parse(src).get('body', 0, 'expression'));
});

it('extracts the documentation for a ClassDeclaration', () => {
const src = `
class Test {
/**
* The foo method
* @protected
* @param bar The bar param
* @returns The number
*/
foo(bar: number): number {
return bar;
}

/**
* Static function
*/
static bar() {}

state = {
foo: 'foo',
};

componentDidMount() {}

render() {
return null;
}
}
`;

test(parse(src).get('body', 0));
});

it('should not find methods for stateless components', () => {
const src = `
(props) => {}
`;

const definition = parse(src).get('body', 0, 'expression');
componentMethodsHandler(documentation, definition);
expect(documentation.methods).toEqual([]);
});
});
74 changes: 74 additions & 0 deletions src/handlers/componentMethodsHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

import recast from 'recast';

import getMemberValuePath from '../utils/getMemberValuePath';
import getMethodDocumentation from '../utils/getMethodDocumentation';
import isReactComponentClass from '../utils/isReactComponentClass';
import isReactComponentMethod from '../utils/isReactComponentMethod';
import isReactCreateClassCall from '../utils/isReactCreateClassCall';

import type Documentation from '../Documentation';

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

function getMethodsDoc(methodPaths) {
const methods = [];

methodPaths.forEach((methodPath) => {
if (isReactComponentMethod(methodPath)) {
return;
}

methods.push(getMethodDocumentation(methodPath));
});

return methods;
}

/**
* Extract all flow types and information from jsdoc for the methods of a
* react component. Doesn't return any react specific lifecycle methods.
*/
export default function componentMethodsHandler(
documentation: Documentation,
path: NodePath
) {
// Extract all methods from the class or object.
let methodPaths;
if (isReactComponentClass(path)) {
methodPaths = path
.get('body', 'body')
.filter(p => types.MethodDefinition.check(p.node) && p.node.kind !== 'constructor');
} else if (types.ObjectExpression.check(path.node)) {
const properties = path.get('properties');

// Add the statics object properties.
const statics = getMemberValuePath(path, 'statics');
if (statics) {
statics.get('properties').each(p => {
p.node.static = true;
properties.push(p.node);
});
}

methodPaths = properties.filter(p => types.FunctionExpression.check(p.get('value').node));
}

if (!methodPaths) {
documentation.set('methods', []);
return;
}

const methods = getMethodsDoc(methodPaths);
documentation.set('methods', methods);
}
1 change: 1 addition & 0 deletions src/handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@


export {default as componentDocblockHandler} from './componentDocblockHandler';
export {default as componentMethodsHandler} from './componentMethodsHandler';
export {default as defaultPropsHandler} from './defaultPropsHandler';
export {default as propTypeHandler} from './propTypeHandler';
export {default as propTypeCompositionHandler} from './propTypeCompositionHandler';
Expand Down
1 change: 1 addition & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var defaultHandlers = [
handlers.defaultPropsHandler,
handlers.componentDocblockHandler,
handlers.displayNameHandler,
handlers.componentMethodsHandler,
];

/**
Expand Down
Loading