Merge pull request #2517 from sahrens/animatedDocGen

[Docs] Expand API parsing and rendering
This commit is contained in:
Spencer Ahrens 2015-09-01 14:15:12 -07:00
commit 2fc8ca01f9
3 changed files with 127 additions and 53 deletions

View File

@ -19,6 +19,7 @@ var genericTransform = require('./generic-function-visitor');
var genericVisitor = genericTransform.visitorList[0]; var genericVisitor = genericTransform.visitorList[0];
var traverseFlat = require('./traverseFlat'); var traverseFlat = require('./traverseFlat');
var parseTypehint = require('./TypeExpressionParser').parse; var parseTypehint = require('./TypeExpressionParser').parse;
var util = require('util');
// Don't save object properties source code that is longer than this // Don't save object properties source code that is longer than this
var MAX_PROPERTY_SOURCE_LENGTH = 1000; var MAX_PROPERTY_SOURCE_LENGTH = 1000;
@ -317,6 +318,7 @@ function getObjectData(node, state, source, scopeChain,
commentsForFile, linesForFile) { commentsForFile, linesForFile) {
var methods = []; var methods = [];
var properties = []; var properties = [];
var classes = [];
var superClass = null; var superClass = null;
node.properties.forEach(function(property) { node.properties.forEach(function(property) {
if (property.type === Syntax.SpreadProperty) { if (property.type === Syntax.SpreadProperty) {
@ -341,7 +343,8 @@ function getObjectData(node, state, source, scopeChain,
scopeChain scopeChain
); );
if (expr) { if (expr) {
if (expr.type === Syntax.FunctionDeclaration) { if (expr.type === Syntax.FunctionDeclaration ||
expr.type === Syntax.FunctionExpression) {
var functionData = var functionData =
getFunctionData(expr, property, state, source, commentsForFile, getFunctionData(expr, property, state, source, commentsForFile,
linesForFile); linesForFile);
@ -362,16 +365,24 @@ function getObjectData(node, state, source, scopeChain,
} }
var docBlock = getDocBlock(property, commentsForFile, linesForFile); var docBlock = getDocBlock(property, commentsForFile, linesForFile);
/* CodexVarDef: modifiers, type, name, default, docblock */ /* CodexVarDef: modifiers, type, name, default, docblock */
var propertyData = [ if (property.value.type === Syntax.ClassDeclaration) {
['static'], var type = {name: property.value.id.name};
'', var classData = getClassData(property.value, state, source, commentsForFile, linesForFile);
classData.ownerProperty = property.key.name;
classes.push(classData);
} else {
var type = {name: property.value.type};
}
var propertyData = {
// Cast to String because this can be a Number // Cast to String because this can be a Number
// Could also be a String literal (e.g. "key") hence the value // Could also be a String literal (e.g. "key") hence the value
String(property.key.name || property.key.value), name: String(property.key.name || property.key.value),
type,
docblock: docBlock || '',
source: source.substring.apply(source, property.range),
modifiers: ['static'],
propertySource, propertySource,
docBlock || '', };
property.loc.start.line
];
properties.push(propertyData); properties.push(propertyData);
break; break;
} }
@ -379,6 +390,7 @@ function getObjectData(node, state, source, scopeChain,
return { return {
methods: methods, methods: methods,
properties: properties, properties: properties,
classes: classes,
superClass: superClass superClass: superClass
}; };
} }
@ -410,6 +422,7 @@ function getClassData(node, state, source, commentsForFile, linesForFile) {
} }
}); });
var data = { var data = {
name: node.id.name,
methods: methods methods: methods
}; };
if (node.superClass && node.superClass.type === Syntax.Identifier) { if (node.superClass && node.superClass.type === Syntax.Identifier) {

View File

@ -20,53 +20,52 @@ var slugify = require('slugify');
var styleReferencePattern = /^[^.]+\.propTypes\.style$/; var styleReferencePattern = /^[^.]+\.propTypes\.style$/;
var ComponentDoc = React.createClass({ function renderType(type) {
renderType: function(type) { if (type.name === 'enum') {
if (type.name === 'enum') { if (typeof type.value === 'string') {
if (typeof type.value === 'string') {
return type.value;
}
return 'enum(' + type.value.map((v => v.value)).join(', ') + ')';
}
if (type.name === 'shape') {
return '{' + Object.keys(type.value).map((key => key + ': ' + this.renderType(type.value[key]))).join(', ') + '}';
}
if (type.name == 'union') {
return type.value.map(this.renderType).join(', ');
}
if (type.name === 'arrayOf') {
return '[' + this.renderType(type.value) + ']';
}
if (type.name === 'instanceOf') {
return type.value; return type.value;
} }
return 'enum(' + type.value.map((v) => v.value).join(', ') + ')';
}
if (type.name === 'custom') { if (type.name === 'shape') {
if (styleReferencePattern.test(type.raw)) { return '{' + Object.keys(type.value).map((key => key + ': ' + renderType(type.value[key]))).join(', ') + '}';
var name = type.raw.substring(0, type.raw.indexOf('.')); }
return <a href={slugify(name) + '.html#style'}>{name}#style</a>
} if (type.name == 'union') {
if (type.raw === 'EdgeInsetsPropType') { return type.value.map(renderType).join(', ');
return '{top: number, left: number, bottom: number, right: number}'; }
}
return type.raw; if (type.name === 'arrayOf') {
return '[' + renderType(type.value) + ']';
}
if (type.name === 'instanceOf') {
return type.value;
}
if (type.name === 'custom') {
if (styleReferencePattern.test(type.raw)) {
var name = type.raw.substring(0, type.raw.indexOf('.'));
return <a href={slugify(name) + '.html#style'}>{name}#style</a>
} }
if (type.raw === 'EdgeInsetsPropType') {
if (type.name === 'stylesheet') { return '{top: number, left: number, bottom: number, right: number}';
return 'style';
} }
return type.raw;
}
if (type.name === 'func') { if (type.name === 'stylesheet') {
return 'function'; return 'style';
} }
return type.name; if (type.name === 'func') {
}, return 'function';
}
return type.name;
}
var ComponentDoc = React.createClass({
renderProp: function(name, prop) { renderProp: function(name, prop) {
return ( return (
<div className="prop" key={name}> <div className="prop" key={name}>
@ -77,7 +76,7 @@ var ComponentDoc = React.createClass({
{name} {name}
{' '} {' '}
{prop.type && <span className="propType"> {prop.type && <span className="propType">
{this.renderType(prop.type)} {renderType(prop.type)}
</span>} </span>}
</Header> </Header>
{prop.type && prop.type.name === 'stylesheet' && {prop.type && prop.type.name === 'stylesheet' &&
@ -128,7 +127,7 @@ var ComponentDoc = React.createClass({
{name} {name}
{' '} {' '}
{style.props[name].type && <span className="propType"> {style.props[name].type && <span className="propType">
{this.renderType(style.props[name].type)} {renderType(style.props[name].type)}
</span>} </span>}
</h6> </h6>
</div> </div>
@ -190,7 +189,6 @@ var ComponentDoc = React.createClass({
<Marked> <Marked>
{content.description} {content.description}
</Marked> </Marked>
<HeaderWithGithub <HeaderWithGithub
title="Props" title="Props"
path={content.filepath} path={content.filepath}
@ -264,7 +262,6 @@ var APIDoc = React.createClass({
); );
}, },
renderMethods: function(methods) { renderMethods: function(methods) {
if (!methods.length) { if (!methods.length) {
return null; return null;
@ -281,6 +278,67 @@ var APIDoc = React.createClass({
); );
}, },
renderProperty: function(property) {
return (
<div className="prop" key={property.name}>
<Header level={4} className="propTitle" toSlug={property.name}>
{property.name}
{property.type &&
<span className="propType">
{': ' + renderType(property.type)}
</span>
}
</Header>
{property.docblock && <Marked>
{this.removeCommentsFromDocblock(property.docblock)}
</Marked>}
</div>
);
},
renderProperties: function(properties) {
if (!properties || !properties.length) {
return null;
}
return (
<span>
<H level={3}>Properties</H>
<div className="props">
{properties.filter((property) => {
return property.name[0] !== '_';
}).map(this.renderProperty)}
</div>
</span>
);
},
renderClasses: function(classes) {
if (!classes || !classes.length) {
return null;
}
return (
<span>
<div>
{classes.filter((cls) => {
return cls.name[0] !== '_' && cls.ownerProperty[0] !== '_';
}).map((cls) => {
return (
<span key={cls.name}>
<Header level={2} toSlug={cls.name}>
class {cls.name}
</Header>
<ul>
{this.renderMethods(cls.methods)}
{this.renderProperties(cls.properties)}
</ul>
</span>
);
})}
</div>
</span>
);
},
render: function() { render: function() {
var content = this.props.content; var content = this.props.content;
if (!content.methods) { if (!content.methods) {
@ -294,6 +352,8 @@ var APIDoc = React.createClass({
{this.removeCommentsFromDocblock(content.docblock)} {this.removeCommentsFromDocblock(content.docblock)}
</Marked> </Marked>
{this.renderMethods(content.methods)} {this.renderMethods(content.methods)}
{this.renderProperties(content.properties)}
{this.renderClasses(content.classes)}
</div> </div>
); );
} }
@ -371,7 +431,7 @@ var Autodocs = React.createClass({
var docs = JSON.parse(this.props.children); var docs = JSON.parse(this.props.children);
var content = docs.type === 'component' || docs.type === 'style' ? var content = docs.type === 'component' || docs.type === 'style' ?
<ComponentDoc content={docs} /> : <ComponentDoc content={docs} /> :
<APIDoc content={docs} />; <APIDoc content={docs} apiName={metadata.title} />;
return ( return (
<Site section="docs" title={metadata.title}> <Site section="docs" title={metadata.title}>

View File

@ -134,7 +134,7 @@ function renderAPI(type) {
try { try {
json = jsDocs(fs.readFileSync(filepath).toString()); json = jsDocs(fs.readFileSync(filepath).toString());
} catch(e) { } catch(e) {
console.error('Cannot parse file', filepath); console.error('Cannot parse file', filepath, e);
json = {}; json = {};
} }
return componentsToMarkdown(type, json, filepath, n++); return componentsToMarkdown(type, json, filepath, n++);
@ -187,6 +187,7 @@ var components = [
var apis = [ var apis = [
'../Libraries/ActionSheetIOS/ActionSheetIOS.js', '../Libraries/ActionSheetIOS/ActionSheetIOS.js',
'../Libraries/Utilities/AlertIOS.js', '../Libraries/Utilities/AlertIOS.js',
'../Libraries/Animated/Animated.js',
'../Libraries/AppRegistry/AppRegistry.js', '../Libraries/AppRegistry/AppRegistry.js',
'../Libraries/AppStateIOS/AppStateIOS.ios.js', '../Libraries/AppStateIOS/AppStateIOS.ios.js',
'../Libraries/Storage/AsyncStorage.ios.js', '../Libraries/Storage/AsyncStorage.ios.js',