Update react-docgen

This commit is contained in:
Felix Kling 2015-03-04 13:37:05 -08:00
parent 7b0cd86759
commit 74824cb96e
18 changed files with 228 additions and 697 deletions

View File

@ -4,7 +4,7 @@
It uses [recast][] to parse the source into an AST and provides methods to process this AST to extract the desired information. The output / return value is a JSON blob / JavaScript object.
It provides a default implementation for React components defined via `React.createClass`. These component definitions must follow certain guidelines in order to be analyzable (see below for more info)
It provides a default implementation for React components defined via `React.createClass`. These component definitions must follow certain guidelines in order to be analyzable (see below for more info).
## Install
@ -38,6 +38,9 @@ If a directory is passed, it is recursively traversed.
By default, `react-docgen` will look for the exported component created through `React.createClass` in each file. Have a look below for how to customize this behavior.
Have a look at `example/` for an example of how to use the result to generate
a markdown version of the documentation.
## API
The tool can be used programmatically to extract component information and customize the extraction process:
@ -209,5 +212,11 @@ The structure of the JSON blob / JavaScript object is as follows:
["composes": <componentNames>]
}
```
(`[...]` means the property may not exist if such information was not found in the component definition)
- `<propName>`: For each prop that was found, there will be an entry in `props` under the same name.
- `<typeName>`: The name of the type, which is usually corresponds to the function name in `React.PropTypes`. However, for types define with `oneOf`, we use `"enum"` and for `oneOfType` we use `"union"`. If a custom function is provided or the type cannot be resolved to anything of `React.PropTypes`, we use `"custom"`.
- `<typeValue>`: Some types accept parameters which define the type in more detail (such as `arrayOf`, `instanceOf`, `oneOf`, etc). Those are stored in `<typeValue>`. The data type of `<typeValue>` depends on the type definition.
[recast]: https://github.com/benjamn/recast

View File

@ -0,0 +1,42 @@
#!/usr/bin/env node
/**
* This example script expects a JSON blob generated by react-docgen as input,
* e.g. react-docgen components/* | buildDocs.sh
*/
var fs = require('fs');
var generateMarkdown = require('./generateMarkdown');
var path = require('path');
var json = '';
process.stdin.setEncoding('utf8');
process.stdin.on('readable', function() {
var chunk = process.stdin.read();
if (chunk !== null) {
json += chunk;
}
});
process.stdin.on('end', function() {
buildDocs(JSON.parse(json));
});
function buildDocs(api) {
// api is an object keyed by filepath. We use the file name as component name.
for (var filepath in api) {
var name = getComponentName(filepath);
var markdown = generateMarkdown(name, api[filepath]);
fs.writeFileSync(name + '.md', markdown);
process.stdout.write(filepath + ' -> ' + name + '.md\n');
}
}
function getComponentName(filepath) {
var name = path.basename(filepath);
var ext;
while ((ext = path.extname(name))) {
name = name.substring(0, name.length - ext.length);
}
return name;
}

View File

@ -0,0 +1,27 @@
var React = require('react');
var Foo = require('Foo');
/**
* General component description.
*/
var Component = React.createClass({
propTypes: {
...Foo.propTypes,
/**
* Prop description
*/
bar: React.PropTypes.number
},
getDefaultProps: function() {
return {
bar: 21
};
},
render: function() {
// ...
}
});
module.exports = Component;

View File

@ -0,0 +1,4 @@
/**
* An example for a module that is not a component.
*/
module.exports = "abc";

View File

@ -0,0 +1,80 @@
/**
* 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.
*/
"use strict";
function stringOfLength(string, length) {
var newString = '';
for (var i = 0; i < length; i++) {
newString += string;
}
return newString;
}
function generateTitle(name) {
var title = '`' + name + '` (component)';
return title + '\n' + stringOfLength('=', title.length) + '\n';
}
function generateDesciption(description) {
return description + '\n';
}
function generatePropType(type) {
var values;
if (Array.isArray(type.value)) {
values = '(' +
type.value.map(function(typeValue) {
return typeValue.name || typeValue.value;
}).join('|') +
')';
} else {
values = type.value;
}
return 'type: `' + type.name + (values ? values: '') + '`\n';
}
function generatePropDefaultValue(value) {
return 'defaultValue: `' + value.value + '`\n';
}
function generateProp(propName, prop) {
return (
'### `' + propName + '`' + (prop.required ? ' (required)' : '') + '\n' +
'\n' +
(prop.description ? prop.description + '\n\n' : '') +
(prop.type ? generatePropType(prop.type) : '') +
(prop.defaultValue ? generatePropDefaultValue(prop.defaultValue) : '') +
'\n'
);
}
function generateProps(props) {
var title = 'Props';
return (
title + '\n' +
stringOfLength('-', title.length) + '\n' +
'\n' +
Object.keys(props).sort().map(function(propName) {
return generateProp(propName, props[propName]);
}).join('\n')
);
}
function generateMarkdown(name, reactAPI) {
var markdownString =
generateTitle(name) + '\n' +
generateDesciption(reactAPI.description) + '\n' +
generateProps(reactAPI.props);
return markdownString;
}
module.exports = generateMarkdown;

View File

@ -1,124 +0,0 @@
/*
* 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
*/
"use strict";
type Handler = (documentation: Documentation, path: NodePath) => void;
var Documentation = require('./Documentation');
var findExportedReactCreateClass =
require('./strategies/findExportedReactCreateClassCall');
var getPropertyName = require('./utils/getPropertyName');
var recast = require('recast');
var resolveToValue = require('./utils/resolveToValue');
var n = recast.types.namedTypes;
class ReactDocumentationParser {
_componentHandlers: Array<Handler>;
_apiHandlers: Object<string, Handler>;
constructor() {
this._componentHandlers = [];
this._apiHandlers = Object.create(null);
}
/**
* Handlers to extract information from the component definition.
*
* If "property" is not provided, the handler is passed the whole component
* definition.
*
* NOTE: The component definition is currently expected to be represented as
* an ObjectExpression (an object literal). This will likely change in the
* future.
*/
addHandler(handler: Handler, property?: string): void {
if (!property) {
this._componentHandlers.push(handler);
} else {
if (!this._apiHandlers[property]) {
this._apiHandlers[property] = [];
}
this._apiHandlers[property].push(handler);
}
}
/**
* Takes JavaScript source code and returns an object with the information
* extract from it.
*
* The second argument is strategy to find the AST node(s) of the component
* definition(s) inside `source`.
* It is a function that gets passed the program AST node of
* the source as first argument, and a reference to recast as second argument.
*
* This allows you define your own strategy for finding component definitions.
* By default it will look for the exported component created by
* React.createClass. An error is thrown if multiple components are exported.
*
* NOTE: The component definition is currently expected to be represented as
* an ObjectExpression (an object literal), no matter which strategy is
* chosen. This will likely change in the future.
*/
parseSource(
source: string,
componentDefinitionStrategy?:
(program: ASTNode, recast: Object) => (Array<NodePath>|NodePath)
): (Array<Object>|Object) {
if (!componentDefinitionStrategy) {
componentDefinitionStrategy = findExportedReactCreateClass;
}
var ast = recast.parse(source);
// Find the component definitions first. The return value should be
// an ObjectExpression.
var componentDefinition = componentDefinitionStrategy(ast.program, recast);
var isArray = Array.isArray(componentDefinition);
if (!componentDefinition || (isArray && componentDefinition.length === 0)) {
throw new Error(ReactDocumentationParser.ERROR_MISSING_DEFINITION);
}
return isArray ?
this._executeHandlers(componentDefinition).map(
documentation => documentation.toObject()
) :
this._executeHandlers([componentDefinition])[0].toObject();
}
_executeHandlers(componentDefinitions: Array<NodePath>): Array<Documenation> {
return componentDefinitions.map(componentDefinition => {
var documentation = new Documentation();
componentDefinition.get('properties').each(propertyPath => {
var name = getPropertyName(propertyPath);
if (!this._apiHandlers[name]) {
return;
}
var propertyValuePath = propertyPath.get('value');
this._apiHandlers[name].forEach(
handler => handler(documentation, propertyValuePath)
);
});
this._componentHandlers.forEach(
handler => handler(documentation, componentDefinition)
);
return documentation;
});
}
}
ReactDocumentationParser.ERROR_MISSING_DEFINITION =
'No suitable component definition found.';
module.exports = ReactDocumentationParser;

View File

@ -1,61 +0,0 @@
/*
* 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.
*
*/
"use strict";
jest.autoMockOff();
describe('React documentation parser', function() {
var ReactDocumentationParser;
var parser;
var recast;
beforeEach(function() {
recast = require('recast');
ReactDocumentationParser = require('../ReactDocumentationParser');
parser = new ReactDocumentationParser();
});
function pathFromSource(source) {
return new recast.types.NodePath(
recast.parse(source).program.body[0].expression
);
}
describe('parseSource', function() {
it('allows custom component definition resolvers', function() {
var path = pathFromSource('({foo: "bar"})');
var resolver = jest.genMockFunction().mockReturnValue(path);
var handler = jest.genMockFunction();
parser.addHandler(handler);
parser.parseSource('', resolver);
expect(resolver).toBeCalled();
expect(handler.mock.calls[0][1]).toBe(path);
});
it('errors if component definition is not found', function() {
var handler = jest.genMockFunction();
expect(function() {
parser.parseSource('', handler);
}).toThrow(ReactDocumentationParser.ERROR_MISSING_DEFINITION);
expect(handler).toBeCalled();
handler = jest.genMockFunction().mockReturnValue([]);
expect(function() {
parser.parseSource('', handler);
}).toThrow(ReactDocumentationParser.ERROR_MISSING_DEFINITION);
expect(handler).toBeCalled();
});
});
});

View File

@ -1,81 +0,0 @@
/*
* 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.
*
*/
"use strict";
jest.autoMockOff();
var module_template = [
'var React = require("React");',
'var PropTypes = React.PropTypes;',
'var Component = React.createClass(%s);',
'module.exports = Component;'
].join('\n');
function getSource(definition) {
return module_template.replace('%s', definition);
}
describe('React documentation parser', function() {
var parser;
beforeEach(function() {
parser = new (require('../../ReactDocumentationParser'));
parser.addHandler(require('../defaultValueHandler'), 'getDefaultProps');
});
it ('should find prop default values that are literals', function() {
var source = getSource([
'{',
' getDefaultProps: function() {',
' return {',
' foo: "bar",',
' bar: 42,',
' baz: ["foo", "bar"],',
' abc: {xyz: abc.def, 123: 42}',
' };',
' }',
'}'
].join('\n'));
var expectedResult = {
description: '',
props: {
foo: {
defaultValue: {
value: '"bar"',
computed: false
}
},
bar: {
defaultValue: {
value: '42',
computed: false
}
},
baz: {
defaultValue: {
value: '["foo", "bar"]',
computed: false
}
},
abc: {
defaultValue: {
value: '{xyz: abc.def, 123: 42}',
computed: false
}
}
}
};
var result = parser.parseSource(source);
expect(result).toEqual(expectedResult);
});
});

View File

@ -25,12 +25,17 @@ describe('propDocblockHandler', function() {
});
function parse(definition) {
return utils.parse('(' + definition + ')').get('body', 0, 'expression');
var programPath = utils.parse(definition);
return programPath.get(
'body',
programPath.node.body.length - 1,
'expression'
);
}
it('finds docblocks for prop types', function() {
var definition = parse([
'{',
'({',
' propTypes: {',
' /**',
' * Foo comment',
@ -42,7 +47,7 @@ describe('propDocblockHandler', function() {
' */',
' bar: Prop.bool,',
' }',
'}'
'})'
].join('\n'));
propDocblockHandler(documentation, definition);
@ -58,7 +63,7 @@ describe('propDocblockHandler', function() {
it('can handle multline comments', function() {
var definition = parse([
'{',
'({',
' propTypes: {',
' /**',
' * Foo comment with',
@ -68,7 +73,7 @@ describe('propDocblockHandler', function() {
' */',
' foo: Prop.bool',
' }',
'}'
'})'
].join('\n'));
propDocblockHandler(documentation, definition);
@ -82,7 +87,7 @@ describe('propDocblockHandler', function() {
it('ignores non-docblock comments', function() {
var definition = parse([
'{',
'({',
' propTypes: {',
' /**',
' * Foo comment',
@ -96,7 +101,7 @@ describe('propDocblockHandler', function() {
' /* This is not a doc comment */',
' bar: Prop.bool,',
' }',
'}'
'})'
].join('\n'));
propDocblockHandler(documentation, definition);
@ -112,7 +117,7 @@ describe('propDocblockHandler', function() {
it('only considers the comment with the property below it', function() {
var definition = parse([
'{',
'({',
' propTypes: {',
' /**',
' * Foo comment',
@ -120,7 +125,7 @@ describe('propDocblockHandler', function() {
' foo: Prop.bool,',
' bar: Prop.bool,',
' }',
'}'
'})'
].join('\n'));
propDocblockHandler(documentation, definition);
@ -136,7 +141,7 @@ describe('propDocblockHandler', function() {
it('understands and ignores the spread operator', function() {
var definition = parse([
'{',
'({',
' propTypes: {',
' ...Foo.propTypes,',
' /**',
@ -144,7 +149,28 @@ describe('propDocblockHandler', function() {
' */',
' foo: Prop.bool,',
' }',
'}'
'})'
].join('\n'));
propDocblockHandler(documentation, definition);
expect(documentation.descriptors).toEqual({
foo: {
description: 'Foo comment'
}
});
});
it('resolves variables', function() {
var definition = parse([
'var Props = {',
' /**',
' * Foo comment',
' */',
' foo: Prop.bool,',
'};',
'({',
' propTypes: Props',
'})'
].join('\n'));
propDocblockHandler(documentation, definition);

View File

@ -171,4 +171,21 @@ describe('propTypeHandler', function() {
},
});
});
it('resolves variables', function() {
var definition = parse([
'var props = {bar: PropTypes.bool};',
'({',
' propTypes: props',
'})',
].join('\n'));
propTypeHandler(documentation, definition);
expect(documentation.descriptors).toEqual({
bar: {
type: {},
required: false
},
});
});
});

View File

@ -1,77 +0,0 @@
/*
* 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
*/
"use strict";
var Documentation = require('../Documentation');
var expressionTo = require('../utils/expressionTo');
var getPropertyName = require('../utils/getPropertyName');
var recast = require('recast');
var resolveToValue = require('../utils/resolveToValue');
var types = recast.types.namedTypes;
var visit = recast.types.visit;
function getDefaultValue(path) {
var node = path.node;
var defaultValue;
if (types.Literal.check(node)) {
defaultValue = node.raw;
} else {
path = resolveToValue(path);
node = path.node;
defaultValue = recast.print(path).code;
}
if (typeof defaultValue !== 'undefined') {
return {
value: defaultValue,
computed: types.CallExpression.check(node) ||
types.MemberExpression.check(node) ||
types.Identifier.check(node)
};
}
}
function defaultValueHandler(documentation: Documentation, path: NodePath) {
if (!types.FunctionExpression.check(path.node)) {
return;
}
// Find the value that is returned from the function and process it if it is
// an object literal.
var objectExpressionPath;
visit(path.get('body'), {
visitFunction: () => false,
visitReturnStatement: function(path) {
var resolvedPath = resolveToValue(path.get('argument'));
if (types.ObjectExpression.check(resolvedPath.node)) {
objectExpressionPath = resolvedPath;
}
return false;
}
});
if (objectExpressionPath) {
objectExpressionPath.get('properties').each(function(propertyPath) {
var propDescriptor = documentation.getPropDescriptor(
getPropertyName(propertyPath)
);
var defaultValue = getDefaultValue(propertyPath.get('value'));
if (defaultValue) {
propDescriptor.defaultValue = defaultValue;
}
});
}
}
module.exports = defaultValueHandler;

View File

@ -15,13 +15,14 @@
var Documentation = require('../Documentation');
var types = require('recast').types.namedTypes;
var getDocblock = require('../utils/docblock').getDocblock;
var getPropertyName = require('../utils/getPropertyName');
var getPropertyValuePath = require('../utils/getPropertyValuePath');
var types = require('recast').types.namedTypes;
var resolveToValue = require('../utils/resolveToValue');
function propDocBlockHandler(documentation: Documentation, path: NodePath) {
var propTypesPath = getPropertyValuePath(path, 'propTypes');
var propTypesPath = resolveToValue(getPropertyValuePath(path, 'propTypes'));
if (!propTypesPath || !types.ObjectExpression.check(propTypesPath.node)) {
return;
}

View File

@ -100,7 +100,7 @@ function amendPropTypes(documentation, path) {
}
function propTypeHandler(documentation: Documentation, path: NodePath) {
var propTypesPath = getPropertyValuePath(resolveToValue(path), 'propTypes');
var propTypesPath = resolveToValue(getPropertyValuePath(path, 'propTypes'));
if (!propTypesPath || !types.ObjectExpression.check(propTypesPath.node)) {
return;
}

View File

@ -1,106 +0,0 @@
/*
* 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.
*
*/
"use strict";
jest.autoMockOff();
describe('React documentation parser', function() {
var findAllReactCreateClassCalls;
var recast;
function parse(source) {
return findAllReactCreateClassCalls(
recast.parse(source).program,
recast
);
}
beforeEach(function() {
findAllReactCreateClassCalls = require('../findAllReactCreateClassCalls');
recast = require('recast');
});
it('finds React.createClass', function() {
var source = [
'var React = require("React");',
'var Component = React.createClass({});',
'module.exports = Component;'
].join('\n');
var result = parse(source);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
expect(result[0] instanceof recast.types.NodePath).toBe(true);
expect(result[0].node.type).toBe('ObjectExpression');
});
it('finds React.createClass, independent of the var name', function() {
var source = [
'var R = require("React");',
'var Component = R.createClass({});',
'module.exports = Component;'
].join('\n');
var result = parse(source);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
});
it('does not process X.createClass of other modules', function() {
var source = [
'var R = require("NoReact");',
'var Component = R.createClass({});',
'module.exports = Component;'
].join('\n');
var result = parse(source);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(0);
});
it('finds assignments to exports', function() {
var source = [
'var R = require("React");',
'var Component = R.createClass({});',
'exports.foo = 42;',
'exports.Component = Component;'
].join('\n');
var result = parse(source);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(1);
});
it('accepts multiple definitions', function() {
var source = [
'var R = require("React");',
'var ComponentA = R.createClass({});',
'var ComponentB = R.createClass({});',
'exports.ComponentB = ComponentB;'
].join('\n');
var result = parse(source);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(2);
source = [
'var R = require("React");',
'var ComponentA = R.createClass({});',
'var ComponentB = R.createClass({});',
'module.exports = ComponentB;'
].join('\n');
result = parse(source);
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(2);
});
});

View File

@ -1,106 +0,0 @@
/*
* 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.
*
*/
"use strict";
jest.autoMockOff();
describe('React documentation parser', function() {
var findExportedReactCreateClass;
var recast;
function parse(source) {
return findExportedReactCreateClass(
recast.parse(source).program,
recast
);
}
beforeEach(function() {
findExportedReactCreateClass =
require('../findExportedReactCreateClassCall');
recast = require('recast');
});
it('finds React.createClass', function() {
var source = [
'var React = require("React");',
'var Component = React.createClass({});',
'module.exports = Component;'
].join('\n');
expect(parse(source)).toBeDefined();
});
it('finds React.createClass, independent of the var name', function() {
var source = [
'var R = require("React");',
'var Component = R.createClass({});',
'module.exports = Component;'
].join('\n');
expect(parse(source)).toBeDefined();
});
it('does not process X.createClass of other modules', function() {
var source = [
'var R = require("NoReact");',
'var Component = R.createClass({});',
'module.exports = Component;'
].join('\n');
expect(parse(source)).toBeUndefined();
});
it('finds assignments to exports', function() {
var source = [
'var R = require("React");',
'var Component = R.createClass({});',
'exports.foo = 42;',
'exports.Component = Component;'
].join('\n');
expect(parse(source)).toBeDefined();
});
it('errors if multiple components are exported', function() {
var source = [
'var R = require("React");',
'var ComponentA = R.createClass({});',
'var ComponentB = R.createClass({});',
'exports.ComponentA = ComponentA;',
'exports.ComponentB = ComponentB;'
].join('\n');
expect(function() {
parse(source)
}).toThrow();
});
it('accepts multiple definitions if only one is exported', function() {
var source = [
'var R = require("React");',
'var ComponentA = R.createClass({});',
'var ComponentB = R.createClass({});',
'exports.ComponentB = ComponentB;'
].join('\n');
expect(parse(source)).toBeDefined();
source = [
'var R = require("React");',
'var ComponentA = R.createClass({});',
'var ComponentB = R.createClass({});',
'module.exports = ComponentB;'
].join('\n');
expect(parse(source)).toBeDefined();
});
});

View File

@ -1,47 +0,0 @@
/*
* 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
*/
"use strict";
var isReactCreateClassCall = require('../utils/isReactCreateClassCall');
var resolveToValue = require('../utils/resolveToValue');
/**
* Given an AST, this function tries to find all object expressions that are
* passed to `React.createClass` calls, by resolving all references properly.
*/
function findAllReactCreateClassCalls(
ast: ASTNode,
recast: Object
): Array<NodePath> {
var types = recast.types.namedTypes;
var definitions = [];
recast.visit(ast, {
visitCallExpression: function(path) {
if (!isReactCreateClassCall(path)) {
return false;
}
// We found React.createClass. Lets get cracking!
var resolvedPath = resolveToValue(path.get('arguments', 0));
if (types.ObjectExpression.check(resolvedPath.node)) {
definitions.push(resolvedPath);
}
return false;
}
});
return definitions;
}
module.exports = findAllReactCreateClassCalls;

View File

@ -1,78 +0,0 @@
/*
* 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
*/
"use strict";
var isExportsOrModuleAssignment =
require('../utils/isExportsOrModuleAssignment');
var isReactCreateClassCall = require('../utils/isReactCreateClassCall');
var resolveToValue = require('../utils/resolveToValue');
var ERROR_MULTIPLE_DEFINITIONS =
'Multiple exported component definitions found.';
function ignore() {
return false;
}
/**
* Given an AST, this function tries to find the object expression that is
* passed to `React.createClass`, by resolving all references properly.
*/
function findExportedReactCreateClass(
ast: ASTNode,
recast: Object
): ?NodePath {
var types = recast.types.namedTypes;
var definition;
recast.visit(ast, {
visitFunctionDeclaration: ignore,
visitFunctionExpression: ignore,
visitIfStatement: ignore,
visitWithStatement: ignore,
visitSwitchStatement: ignore,
visitCatchCause: ignore,
visitWhileStatement: ignore,
visitDoWhileStatement: ignore,
visitForStatement: ignore,
visitForInStatement: ignore,
visitAssignmentExpression: function(path) {
// Ignore anything that is not `exports.X = ...;` or
// `module.exports = ...;`
if (!isExportsOrModuleAssignment(path)) {
return false;
}
// Resolve the value of the right hand side. It should resolve to a call
// expression, something like React.createClass
path = resolveToValue(path.get('right'));
if (!isReactCreateClassCall(path)) {
return false;
}
if (definition) {
// If a file exports multiple components, ... complain!
throw new Error(ERROR_MULTIPLE_DEFINITIONS);
}
// We found React.createClass. Lets get cracking!
var resolvedPath = resolveToValue(path.get('arguments', 0));
if (types.ObjectExpression.check(resolvedPath.node)) {
definition = resolvedPath;
}
return false;
}
});
return definition;
}
module.exports = findExportedReactCreateClass;

View File

@ -1,7 +1,12 @@
{
"name": "react-docgen",
"version": "1.0.0",
"description": "Extract information from React components for documentation generation",
"description": "A CLI and toolkit to extract information from React components for documentation generation.",
"repository": {
"type": "git",
"url": "https://github.com/reactjs/react-docgen.git"
},
"bugs": "https://github.com/reactjs/react-docgen/issues",
"bin": {
"react-docgen": "bin/react-docgen.js"
},
@ -14,7 +19,7 @@
},
"keywords": [
"react",
"documentation"
"documentation-generation"
],
"author": "Felix Kling",
"license": "BSD-3-Clause",