Improve autogen for reference docs including jsdoc support
Summary: As part of improving the API and Component reference docs #8154 this pull request adds the following: - jsdoc support for API docs. See the AlertIOS changes as an example. - type definitions support and added to both API and Component docs. This is supported via react-docgen and jsdoc. - better formatting of method properties (now shown in a table). FYI, API and Component docs were previously generated in two different ways. Components were using react-docgen and that basically remains as-is. APIs were using custom parsing code and that's been switched to use a jsdoc parser + react-docgen as an option for typedefs (it could also use the jsdoc parser). Two docs have been updated to showcase how we'd like the new docs to look: - AlertIOS (API): showing method parameters, examples, typedefs, more details overall. - Statusbar (Component): showing method parameters, typedefs, more details overall. **Note**: To convert new API docs to use the new format, add `jsdoc` to the initial file comment. C Closes https://github.com/facebook/react-native/pull/8196 Differential Revision: D3465037 Pulled By: lacker fbshipit-source-id: 78415d44bc5be02db802f5b1f7a0b249689abdf7
This commit is contained in:
parent
bdb8efdd66
commit
22f59a88a6
|
@ -19,14 +19,35 @@ const processColor = require('processColor');
|
|||
|
||||
const StatusBarManager = require('NativeModules').StatusBarManager;
|
||||
|
||||
/**
|
||||
* Status bar style
|
||||
*/
|
||||
export type StatusBarStyle = $Enum<{
|
||||
/**
|
||||
* Default status bar style
|
||||
*/
|
||||
'default': string,
|
||||
/**
|
||||
* Dark background style
|
||||
*/
|
||||
'light-content': string,
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Status bar animation
|
||||
*/
|
||||
export type StatusBarAnimation = $Enum<{
|
||||
/**
|
||||
* No animation
|
||||
*/
|
||||
'none': string,
|
||||
/**
|
||||
* Fade animation
|
||||
*/
|
||||
'fade': string,
|
||||
/**
|
||||
* Slide animation
|
||||
*/
|
||||
'slide': string,
|
||||
}>;
|
||||
|
||||
|
@ -135,6 +156,13 @@ const StatusBar = React.createClass({
|
|||
|
||||
// Provide an imperative API as static functions of the component.
|
||||
// See the corresponding prop for more detail.
|
||||
|
||||
/**
|
||||
* Show or hide the status bar
|
||||
* @param hidden The dialog's title.
|
||||
* @param animation Optional animation when
|
||||
* changing the status bar hidden property.
|
||||
*/
|
||||
setHidden(hidden: boolean, animation?: StatusBarAnimation) {
|
||||
animation = animation || 'none';
|
||||
StatusBar._defaultProps.hidden.value = hidden;
|
||||
|
@ -145,6 +173,11 @@ const StatusBar = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the status bar style
|
||||
* @param style Status bar style to set
|
||||
* @param animated Animate the style change.
|
||||
*/
|
||||
setBarStyle(style: StatusBarStyle, animated?: boolean) {
|
||||
if (Platform.OS !== 'ios') {
|
||||
console.warn('`setBarStyle` is only available on iOS');
|
||||
|
@ -155,6 +188,10 @@ const StatusBar = React.createClass({
|
|||
StatusBarManager.setStyle(style, animated);
|
||||
},
|
||||
|
||||
/**
|
||||
* Control the visibility of the network activity indicator
|
||||
* @param visible Show the indicator.
|
||||
*/
|
||||
setNetworkActivityIndicatorVisible(visible: boolean) {
|
||||
if (Platform.OS !== 'ios') {
|
||||
console.warn('`setNetworkActivityIndicatorVisible` is only available on iOS');
|
||||
|
@ -164,6 +201,11 @@ const StatusBar = React.createClass({
|
|||
StatusBarManager.setNetworkActivityIndicatorVisible(visible);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the background color for the status bar
|
||||
* @param color Background color.
|
||||
* @param animated Animate the style change.
|
||||
*/
|
||||
setBackgroundColor(color: string, animated?: boolean) {
|
||||
if (Platform.OS !== 'android') {
|
||||
console.warn('`setBackgroundColor` is only available on Android');
|
||||
|
@ -174,6 +216,10 @@ const StatusBar = React.createClass({
|
|||
StatusBarManager.setColor(processColor(color), animated);
|
||||
},
|
||||
|
||||
/**
|
||||
* Control the translucency of the status bar
|
||||
* @param translucent Set as translucent.
|
||||
*/
|
||||
setTranslucent(translucent: boolean) {
|
||||
if (Platform.OS !== 'android') {
|
||||
console.warn('`setTranslucent` is only available on Android');
|
||||
|
|
|
@ -8,64 +8,129 @@
|
|||
*
|
||||
* @providesModule AlertIOS
|
||||
* @flow
|
||||
* @jsdoc
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RCTAlertManager = require('NativeModules').AlertManager;
|
||||
|
||||
/**
|
||||
* An Alert button type
|
||||
*/
|
||||
export type AlertType = $Enum<{
|
||||
/**
|
||||
* Default alert with no inputs
|
||||
*/
|
||||
'default': string;
|
||||
/**
|
||||
* Plain text input alert
|
||||
*/
|
||||
'plain-text': string;
|
||||
/**
|
||||
* Secure text input alert
|
||||
*/
|
||||
'secure-text': string;
|
||||
/**
|
||||
* Login and password alert
|
||||
*/
|
||||
'login-password': string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* An Alert button style
|
||||
*/
|
||||
export type AlertButtonStyle = $Enum<{
|
||||
/**
|
||||
* Default button style
|
||||
*/
|
||||
'default': string;
|
||||
/**
|
||||
* Cancel button style
|
||||
*/
|
||||
'cancel': string;
|
||||
/**
|
||||
* Destructive button style
|
||||
*/
|
||||
'destructive': string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Array or buttons
|
||||
* @typedef {Array} ButtonsArray
|
||||
* @property {string=} text Button label
|
||||
* @property {Function=} onPress Callback function when button pressed
|
||||
* @property {AlertButtonStyle=} style Button style
|
||||
*/
|
||||
type ButtonsArray = Array<{
|
||||
/**
|
||||
* Button label
|
||||
*/
|
||||
text?: string;
|
||||
/**
|
||||
* Callback function when button pressed
|
||||
*/
|
||||
onPress?: ?Function;
|
||||
/**
|
||||
* Button style
|
||||
*/
|
||||
style?: AlertButtonStyle;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* The AlertsIOS utility provides two functions: `alert` and `prompt`. All
|
||||
* functionality available through `AlertIOS.alert` is also available in the
|
||||
* cross-platform `Alert.alert`, which we recommend you use if you don't need
|
||||
* iOS-specific functionality.
|
||||
* @description
|
||||
* `AlertIOS` provides functionality to create an iOS alert dialog with a
|
||||
* message or create a prompt for user input.
|
||||
*
|
||||
* `AlertIOS.prompt` allows you to prompt the user for input inside of an
|
||||
* alert popup.
|
||||
* Creating an iOS alert:
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.alert(
|
||||
* 'Sync Complete',
|
||||
* 'All your data are belong to us.'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* Creating an iOS prompt:
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.prompt(
|
||||
* 'Enter a value',
|
||||
* null,
|
||||
* text => console.log("You entered "+text)
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* We recommend using the [`Alert.alert`](/docs/alert.html) method for
|
||||
* cross-platform support if you don't need to create iOS-only prompts.
|
||||
*
|
||||
*/
|
||||
class AlertIOS {
|
||||
/**
|
||||
* Creates a popup to alert the user. See
|
||||
* [Alert](docs/alert.html).
|
||||
*
|
||||
* - title: string -- The dialog's title.
|
||||
* - message: string -- An optional message that appears above the text input.
|
||||
* - callbackOrButtons -- This optional argument should be either a
|
||||
* single-argument function or an array of buttons. If passed a function,
|
||||
* it will be called when the user taps 'OK'.
|
||||
* Create and display a popup alert.
|
||||
* @static
|
||||
* @method alert
|
||||
* @param title The dialog's title.
|
||||
* @param message An optional message that appears below
|
||||
* the dialog's title.
|
||||
* @param callbackOrButtons This optional argument should
|
||||
* be either a single-argument function or an array of buttons. If passed
|
||||
* a function, it will be called when the user taps 'OK'.
|
||||
*
|
||||
* If passed an array of button configurations, each button should include
|
||||
* a `text` key, as well as optional `onPress` and `style` keys.
|
||||
* `style` should be one of 'default', 'cancel' or 'destructive'.
|
||||
* - type -- *deprecated, do not use*
|
||||
* a `text` key, as well as optional `onPress` and `style` keys. `style`
|
||||
* should be one of 'default', 'cancel' or 'destructive'.
|
||||
* @param type Deprecated, do not use.
|
||||
*
|
||||
* Example:
|
||||
* @example <caption>Example with custom buttons</caption>
|
||||
*
|
||||
* ```
|
||||
* AlertIOS.alert(
|
||||
* 'Sync Complete',
|
||||
* 'All your data are belong to us.'
|
||||
* 'Update available',
|
||||
* 'Keep your app up to date to enjoy the latest features',
|
||||
* [
|
||||
* {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
|
||||
* {text: 'Install', onPress: () => console.log('Install Pressed')},
|
||||
* ],
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
static alert(
|
||||
title: ?string,
|
||||
|
@ -82,23 +147,26 @@ class AlertIOS {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to enter some text.
|
||||
*
|
||||
* - title: string -- The dialog's title.
|
||||
* - message: string -- An optional message that appears above the text input.
|
||||
* - callbackOrButtons -- This optional argument should be either a
|
||||
* single-argument function or an array of buttons. If passed a function,
|
||||
* it will be called with the prompt's value when the user taps 'OK'.
|
||||
* Create and display a prompt to enter some text.
|
||||
* @static
|
||||
* @method prompt
|
||||
* @param title The dialog's title.
|
||||
* @param message An optional message that appears above the text
|
||||
* input.
|
||||
* @param callbackOrButtons This optional argument should
|
||||
* be either a single-argument function or an array of buttons. If passed
|
||||
* a function, it will be called with the prompt's value when the user
|
||||
* taps 'OK'.
|
||||
*
|
||||
* If passed an array of button configurations, each button should include
|
||||
* a `text` key, as well as optional `onPress` and `style` keys (see example).
|
||||
* `style` should be one of 'default', 'cancel' or 'destructive'.
|
||||
* - type: string -- This configures the text input. One of 'plain-text',
|
||||
* a `text` key, as well as optional `onPress` and `style` keys (see
|
||||
* example). `style` should be one of 'default', 'cancel' or 'destructive'.
|
||||
* @param type This configures the text input. One of 'plain-text',
|
||||
* 'secure-text' or 'login-password'.
|
||||
* - defaultValue: string -- the default value for the text field.
|
||||
* @param defaultValue The dialog's title.
|
||||
*
|
||||
* @example <caption>Example with custom buttons</caption>
|
||||
*
|
||||
* Example with custom buttons:
|
||||
* ```
|
||||
* AlertIOS.prompt(
|
||||
* 'Enter password',
|
||||
* 'Enter your password to claim your $1.5B in lottery winnings',
|
||||
|
@ -108,18 +176,16 @@ class AlertIOS {
|
|||
* ],
|
||||
* 'secure-text'
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* Example with the default button and a custom callback:
|
||||
* ```
|
||||
* @example <caption>Example with the default button and a custom callback</caption>
|
||||
*
|
||||
* AlertIOS.prompt(
|
||||
* 'Update username',
|
||||
* null,
|
||||
* text => console.log("Your username is "+text),
|
||||
* null,
|
||||
* 'default'
|
||||
* )
|
||||
* ```
|
||||
* );
|
||||
*/
|
||||
static prompt(
|
||||
title: ?string,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"tags": {
|
||||
"allowUnknownTags": true,
|
||||
"dictionaries": ["jsdoc","closure"]
|
||||
},
|
||||
"source": {
|
||||
"includePattern": ".+\\.js(doc)?$",
|
||||
"excludePattern": "(^|\\/|\\\\)_"
|
||||
},
|
||||
"plugins": ["./jsdocs/jsdoc-plugin-values"],
|
||||
"templates": {
|
||||
"cleverLinks": true,
|
||||
"monospaceLinks": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
exports.defineTags = function(dictionary) {
|
||||
dictionary.defineTag('value', {
|
||||
mustHaveValue: true,
|
||||
canHaveType: true,
|
||||
canHaveName: true,
|
||||
onTagged: function(doclet, tag) {
|
||||
if (!doclet.values) { doclet.values = []; }
|
||||
doclet.values.push(tag.value);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -9,6 +9,8 @@
|
|||
* @providesModule AutodocsLayout
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var DocsSidebar = require('DocsSidebar');
|
||||
var H = require('Header');
|
||||
var Header = require('Header');
|
||||
|
@ -93,6 +95,45 @@ function renderType(type) {
|
|||
return type.name;
|
||||
}
|
||||
|
||||
function renderTypeNameLink(typeName, docPath, namedTypes) {
|
||||
const ignoreTypes = [
|
||||
'string',
|
||||
'number',
|
||||
'boolean',
|
||||
'object',
|
||||
'function',
|
||||
'array',
|
||||
];
|
||||
const typeNameLower = typeName.toLowerCase();
|
||||
if (ignoreTypes.indexOf(typeNameLower) !== -1 || !namedTypes[typeNameLower]) {
|
||||
return typeName;
|
||||
}
|
||||
return <a href={docPath + '#' + typeNameLower}>{typeName}</a>;
|
||||
}
|
||||
|
||||
function renderTypeWithLinks(type, docTitle, namedTypes) {
|
||||
if (!type || !type.names) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const docPath = docTitle ? 'docs/' + docTitle.toLowerCase() + '.html' : 'docs/';
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
type.names.map((typeName, index, array) => {
|
||||
let separator = index < array.length - 1 && ' | ';
|
||||
return (
|
||||
<span key={index}>
|
||||
{renderTypeNameLink(typeName, docPath, namedTypes)}
|
||||
{separator}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function sortByPlatform(props, nameA, nameB) {
|
||||
var a = props[nameA];
|
||||
var b = props[nameB];
|
||||
|
@ -134,6 +175,17 @@ function removeCommentsFromDocblock(docblock) {
|
|||
.join('\n');
|
||||
}
|
||||
|
||||
function getNamedTypes(typedefs) {
|
||||
let namedTypes = {};
|
||||
typedefs && typedefs.forEach(typedef => {
|
||||
if (typedef.name) {
|
||||
const type = typedef.name.toLowerCase();
|
||||
namedTypes[type] = 1;
|
||||
}
|
||||
});
|
||||
return namedTypes;
|
||||
}
|
||||
|
||||
var ComponentDoc = React.createClass({
|
||||
renderProp: function(name, prop) {
|
||||
return (
|
||||
|
@ -254,20 +306,22 @@ var ComponentDoc = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
renderMethod: function(method) {
|
||||
renderMethod: function(method, namedTypes) {
|
||||
return (
|
||||
<Method
|
||||
key={method.name}
|
||||
name={method.name}
|
||||
description={method.description}
|
||||
params={method.params}
|
||||
modifiers={method.modifiers}
|
||||
modifiers={method.scope ? [method.scope] : method.modifiers}
|
||||
examples={method.examples}
|
||||
returns={method.returns}
|
||||
namedTypes={namedTypes}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderMethods: function(methods) {
|
||||
renderMethods: function(methods, namedTypes) {
|
||||
if (!methods || !methods.length) {
|
||||
return null;
|
||||
}
|
||||
|
@ -277,7 +331,38 @@ var ComponentDoc = React.createClass({
|
|||
<div className="props">
|
||||
{methods.filter((method) => {
|
||||
return method.name[0] !== '_';
|
||||
}).map(this.renderMethod)}
|
||||
}).map(method => this.renderMethod(method, namedTypes))}
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
renderTypeDef: function(typedef, namedTypes) {
|
||||
return (
|
||||
<TypeDef
|
||||
key={typedef.name}
|
||||
name={typedef.name}
|
||||
description={typedef.description}
|
||||
type={typedef.type}
|
||||
properties={typedef.properties}
|
||||
values={typedef.values}
|
||||
apiName={this.props.apiName}
|
||||
namedTypes={namedTypes}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderTypeDefs: function(typedefs, namedTypes) {
|
||||
if (!typedefs || !typedefs.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<H level={3}>Type Definitions</H>
|
||||
<div className="props">
|
||||
{typedefs.map((typedef) => {
|
||||
return this.renderTypeDef(typedef, namedTypes);
|
||||
})}
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
|
@ -286,6 +371,7 @@ var ComponentDoc = React.createClass({
|
|||
render: function() {
|
||||
var content = this.props.content;
|
||||
this.extractPlatformFromProps(content.props);
|
||||
const namedTypes = getNamedTypes(content.typedef);
|
||||
return (
|
||||
<div>
|
||||
<Marked>
|
||||
|
@ -293,7 +379,8 @@ var ComponentDoc = React.createClass({
|
|||
</Marked>
|
||||
<H level={3}>Props</H>
|
||||
{this.renderProps(content.props, content.composes)}
|
||||
{this.renderMethods(content.methods)}
|
||||
{this.renderMethods(content.methods, namedTypes)}
|
||||
{this.renderTypeDefs(content.typedef, namedTypes)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -301,19 +388,22 @@ var ComponentDoc = React.createClass({
|
|||
|
||||
var APIDoc = React.createClass({
|
||||
|
||||
renderMethod: function(method) {
|
||||
renderMethod: function(method, namedTypes) {
|
||||
return (
|
||||
<Method
|
||||
key={method.name}
|
||||
name={method.name}
|
||||
description={method.docblock && removeCommentsFromDocblock(method.docblock)}
|
||||
description={method.description || method.docblock && removeCommentsFromDocblock(method.docblock)}
|
||||
params={method.params}
|
||||
modifiers={method.modifiers}
|
||||
modifiers={method.scope ? [method.scope] : method.modifiers}
|
||||
examples={method.examples}
|
||||
apiName={this.props.apiName}
|
||||
namedTypes={namedTypes}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderMethods: function(methods) {
|
||||
renderMethods: function(methods, namedTypes) {
|
||||
if (!methods.length) {
|
||||
return null;
|
||||
}
|
||||
|
@ -323,7 +413,7 @@ var APIDoc = React.createClass({
|
|||
<div className="props">
|
||||
{methods.filter((method) => {
|
||||
return method.name[0] !== '_';
|
||||
}).map(this.renderMethod)}
|
||||
}).map(method => this.renderMethod(method, namedTypes))}
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
|
@ -363,7 +453,7 @@ var APIDoc = React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
renderClasses: function(classes) {
|
||||
renderClasses: function(classes, namedTypes) {
|
||||
if (!classes || !classes.length) {
|
||||
return null;
|
||||
}
|
||||
|
@ -382,7 +472,7 @@ var APIDoc = React.createClass({
|
|||
{cls.docblock && <Marked>
|
||||
{removeCommentsFromDocblock(cls.docblock)}
|
||||
</Marked>}
|
||||
{this.renderMethods(cls.methods)}
|
||||
{this.renderMethods(cls.methods, namedTypes)}
|
||||
{this.renderProperties(cls.properties)}
|
||||
</ul>
|
||||
</span>
|
||||
|
@ -393,6 +483,55 @@ var APIDoc = React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
renderTypeDef: function(typedef, namedTypes) {
|
||||
return (
|
||||
<TypeDef
|
||||
key={typedef.name}
|
||||
name={typedef.name}
|
||||
description={typedef.description}
|
||||
type={typedef.type}
|
||||
properties={typedef.properties}
|
||||
values={typedef.values}
|
||||
apiName={this.props.apiName}
|
||||
namedTypes={namedTypes}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
renderTypeDefs: function(typedefs, namedTypes) {
|
||||
if (!typedefs || !typedefs.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
<H level={3}>Type Definitions</H>
|
||||
<div className="props">
|
||||
{typedefs.map((typedef) => {
|
||||
return this.renderTypeDef(typedef, namedTypes);
|
||||
})}
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
renderMainDescription: function(content) {
|
||||
if (content.docblock) {
|
||||
return (
|
||||
<Marked>
|
||||
{removeCommentsFromDocblock(content.docblock)}
|
||||
</Marked>
|
||||
);
|
||||
}
|
||||
if (content.class && content.class.length && content.class[0].description) {
|
||||
return (
|
||||
<Marked>
|
||||
{content.class[0].description}
|
||||
</Marked>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var content = this.props.content;
|
||||
if (!content.methods) {
|
||||
|
@ -400,14 +539,14 @@ var APIDoc = React.createClass({
|
|||
'No component methods found for ' + content.componentName
|
||||
);
|
||||
}
|
||||
const namedTypes = getNamedTypes(content.typedef);
|
||||
return (
|
||||
<div>
|
||||
<Marked>
|
||||
{removeCommentsFromDocblock(content.docblock)}
|
||||
</Marked>
|
||||
{this.renderMethods(content.methods)}
|
||||
{this.renderMainDescription(content)}
|
||||
{this.renderMethods(content.methods, namedTypes)}
|
||||
{this.renderProperties(content.properties)}
|
||||
{this.renderClasses(content.classes)}
|
||||
{this.renderClasses(content.classes, namedTypes)}
|
||||
{this.renderTypeDefs(content.typedef, namedTypes)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -440,11 +579,73 @@ var Method = React.createClass({
|
|||
return this.renderTypehintRec(typehint);
|
||||
},
|
||||
|
||||
renderMethodExamples: function(examples) {
|
||||
if (!examples || !examples.length) {
|
||||
return null;
|
||||
}
|
||||
return examples.map((example) => {
|
||||
const re = /<caption>(.*?)<\/caption>/ig;
|
||||
const result = re.exec(example);
|
||||
const caption = result ? result[1] + ':' : 'Example:';
|
||||
const code = example.replace(/<caption>.*?<\/caption>/ig, '')
|
||||
.replace(/^\n\n/, '');
|
||||
return (
|
||||
<div>
|
||||
<br/>
|
||||
{caption}
|
||||
<Prism>
|
||||
{code}
|
||||
</Prism>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
renderMethodParameters: function(params) {
|
||||
if (!params || !params.length) {
|
||||
return null;
|
||||
}
|
||||
if (!params[0].type || !params[0].type.names) {
|
||||
return null;
|
||||
}
|
||||
const foundDescription = params.find(p => p.description);
|
||||
if (!foundDescription) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<strong>Parameters:</strong>
|
||||
<table className="params">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name and Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{params.map((param) => {
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
{param.optional ? '[' + param.name + ']' : param.name}
|
||||
<br/><br/>
|
||||
{renderTypeWithLinks(param.type, this.props.apiName, this.props.namedTypes)}
|
||||
</td>
|
||||
<td className="description"><Marked>{param.description}</Marked></td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="prop">
|
||||
<Header level={4} className="propTitle" toSlug={this.props.name}>
|
||||
{this.props.modifiers.length && <span className="propType">
|
||||
{this.props.modifiers && this.props.modifiers.length && <span className="propType">
|
||||
{this.props.modifiers.join(' ') + ' '}
|
||||
</span> || ''}
|
||||
{this.props.name}
|
||||
|
@ -452,9 +653,7 @@ var Method = React.createClass({
|
|||
({this.props.params
|
||||
.map((param) => {
|
||||
var res = param.name;
|
||||
if (param.type) {
|
||||
res += ': ' + this.renderTypehint(param.type);
|
||||
}
|
||||
res += param.optional ? '?' : '';
|
||||
return res;
|
||||
})
|
||||
.join(', ')})
|
||||
|
@ -464,6 +663,100 @@ var Method = React.createClass({
|
|||
{this.props.description && <Marked>
|
||||
{this.props.description}
|
||||
</Marked>}
|
||||
{this.renderMethodParameters(this.props.params)}
|
||||
{this.renderMethodExamples(this.props.examples)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var TypeDef = React.createClass({
|
||||
renderProperties: function(properties) {
|
||||
if (!properties || !properties.length) {
|
||||
return null;
|
||||
}
|
||||
if (!properties[0].type || !properties[0].type.names) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<br/>
|
||||
<strong>Properties:</strong>
|
||||
<table className="params">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name and Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{properties.map((property) => {
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
{property.optional ? '[' + property.name + ']' : property.name}
|
||||
<br/><br/>
|
||||
{renderTypeWithLinks(property.type, this.props.apiName, this.props.namedTypes)}
|
||||
</td>
|
||||
<td className="description"><Marked>{property.description}</Marked></td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
renderValues: function(values) {
|
||||
if (!values || !values.length) {
|
||||
return null;
|
||||
}
|
||||
if (!values[0].type || !values[0].type.names) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<br/>
|
||||
<strong>Constants:</strong>
|
||||
<table className="params">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{values.map((value) => {
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
{value.name}
|
||||
</td>
|
||||
<td className="description"><Marked>{value.description}</Marked></td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="prop">
|
||||
<Header level={4} className="propTitle" toSlug={this.props.name}>
|
||||
{this.props.name}
|
||||
</Header>
|
||||
{this.props.description && <Marked>
|
||||
{this.props.description}
|
||||
</Marked>}
|
||||
<strong>Type:</strong>
|
||||
<br/>
|
||||
{this.props.type.names.join(' | ')}
|
||||
{this.renderProperties(this.props.properties)}
|
||||
{this.renderValues(this.props.values)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -13,9 +13,17 @@
|
|||
"mkdirp": "^0.5.1",
|
||||
"optimist": "0.6.0",
|
||||
"react": "~0.13.0",
|
||||
"react-docgen": "^2.8.0",
|
||||
"react-docgen": "^2.9.0",
|
||||
"react-page-middleware": "git://github.com/facebook/react-page-middleware.git",
|
||||
"request": "^2.69.0",
|
||||
"semver-compare": "^1.0.0"
|
||||
"semver-compare": "^1.0.0",
|
||||
"babel-core": "^6.6.4",
|
||||
"babel-plugin-external-helpers": "^6.5.0",
|
||||
"babel-polyfill": "^6.6.1",
|
||||
"babel-preset-react-native": "~1.6.0",
|
||||
"babel-register": "^6.6.0",
|
||||
"babel-types": "^6.6.4",
|
||||
"jsdoc-api": "^1.1.0",
|
||||
"deep-assign": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"use strict";
|
||||
var docgen = require('react-docgen');
|
||||
'use strict';
|
||||
const docgen = require('react-docgen');
|
||||
|
||||
function stylePropTypeHandler(documentation, path) {
|
||||
var propTypesPath = docgen.utils.getMemberValuePath(path, 'propTypes');
|
||||
let propTypesPath = docgen.utils.getMemberValuePath(path, 'propTypes');
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
|
@ -18,25 +18,25 @@ function stylePropTypeHandler(documentation, path) {
|
|||
docgen.utils.getPropertyName(propertyPath) !== 'style') {
|
||||
return;
|
||||
}
|
||||
var valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
|
||||
let valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
|
||||
// If it's a call to StyleSheetPropType, do stuff
|
||||
if (valuePath.node.type !== 'CallExpression' ||
|
||||
valuePath.node.callee.name !== 'StyleSheetPropType') {
|
||||
return;
|
||||
}
|
||||
// Get type of style sheet
|
||||
var styleSheetModule = docgen.utils.resolveToModule(
|
||||
let styleSheetModule = docgen.utils.resolveToModule(
|
||||
valuePath.get('arguments', 0)
|
||||
);
|
||||
if (styleSheetModule) {
|
||||
var propDescriptor = documentation.getPropDescriptor('style');
|
||||
let propDescriptor = documentation.getPropDescriptor('style');
|
||||
propDescriptor.type = {name: 'stylesheet', value: styleSheetModule};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deprecatedPropTypeHandler(documentation, path) {
|
||||
var propTypesPath = docgen.utils.getMemberValuePath(path, 'propTypes');
|
||||
let propTypesPath = docgen.utils.getMemberValuePath(path, 'propTypes');
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
|
@ -48,13 +48,13 @@ function deprecatedPropTypeHandler(documentation, path) {
|
|||
|
||||
// Checks for deprecatedPropType function and add deprecation info.
|
||||
propTypesPath.get('properties').each(function(propertyPath) {
|
||||
var valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
|
||||
let valuePath = docgen.utils.resolveToValue(propertyPath.get('value'));
|
||||
// If it's a call to deprecatedPropType, do stuff
|
||||
if (valuePath.node.type !== 'CallExpression' ||
|
||||
valuePath.node.callee.name !== 'deprecatedPropType') {
|
||||
return;
|
||||
}
|
||||
var propDescriptor = documentation.getPropDescriptor(
|
||||
let propDescriptor = documentation.getPropDescriptor(
|
||||
docgen.utils.getPropertyName(propertyPath)
|
||||
);
|
||||
// The 2nd argument of deprecatedPropType is the deprecation message.
|
||||
|
@ -72,13 +72,104 @@ function deprecatedPropTypeHandler(documentation, path) {
|
|||
});
|
||||
}
|
||||
|
||||
function typedefHandler(documentation, path) {
|
||||
const declarationPath = path.get('declaration');
|
||||
const typePath = declarationPath.get('right');
|
||||
|
||||
// Name, type, description of the typedef
|
||||
const name = declarationPath.value.id.name;
|
||||
const type = { names: [typePath.node.id.name] };
|
||||
const description = docgen.utils.docblock.getDocblock(path);
|
||||
|
||||
// Get the properties for the typedef
|
||||
let paramsDescriptions = [];
|
||||
let paramsTypes;
|
||||
if (typePath.node.typeParameters) {
|
||||
const paramsPath = typePath.get('typeParameters').get('params', 0);
|
||||
if (paramsPath) {
|
||||
const properties = paramsPath.get('properties');
|
||||
// Get the descriptions inside each property (are inline leading comments)
|
||||
paramsDescriptions =
|
||||
properties.map(property => docgen.utils.docblock.getDocblock(property));
|
||||
// Get the property type info
|
||||
paramsTypes = docgen.utils.getFlowType(paramsPath);
|
||||
}
|
||||
}
|
||||
// Get the property type, description and value info
|
||||
let values = [];
|
||||
if (paramsTypes && paramsTypes.signature && paramsTypes.signature.properties &&
|
||||
paramsTypes.signature.properties.length !== 0) {
|
||||
values = paramsTypes.signature.properties.map((property, index) => {
|
||||
return {
|
||||
type: { names: [property.value.name] },
|
||||
description: paramsDescriptions[index],
|
||||
name: property.key,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let typedef = {
|
||||
name: name,
|
||||
description: description,
|
||||
type: type,
|
||||
values: values,
|
||||
};
|
||||
documentation.set('typedef', typedef);
|
||||
}
|
||||
|
||||
function getTypeName(type) {
|
||||
let typeName;
|
||||
switch (type.name) {
|
||||
case 'signature':
|
||||
typeName = type.type;
|
||||
break;
|
||||
case 'union':
|
||||
typeName = type.elements.map(getTypeName);
|
||||
break;
|
||||
default:
|
||||
typeName = type.alias ? type.alias : type.name;
|
||||
break;
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
function jsDocFormatType(entities) {
|
||||
let modEntities = entities;
|
||||
if (entities) {
|
||||
if (typeof entities === 'object' && entities.length) {
|
||||
entities.map((entity, entityIndex) => {
|
||||
if (entity.type) {
|
||||
const typeNames = [].concat(getTypeName(entity.type));
|
||||
modEntities[entityIndex].type = { names: typeNames };
|
||||
}
|
||||
});
|
||||
} else {
|
||||
modEntities.type = [].concat(getTypeName(entities));
|
||||
}
|
||||
}
|
||||
return modEntities;
|
||||
}
|
||||
|
||||
function jsDocFormatHandler(documentation, path) {
|
||||
const methods = documentation.get('methods');
|
||||
if (!methods || methods.length === 0) {
|
||||
return;
|
||||
}
|
||||
let modMethods = methods;
|
||||
methods.map((method, methodIndex) => {
|
||||
modMethods[methodIndex].params = jsDocFormatType(method.params);
|
||||
modMethods[methodIndex].returns = jsDocFormatType(method.returns);
|
||||
});
|
||||
documentation.set('methods', modMethods);
|
||||
}
|
||||
|
||||
function findExportedOrFirst(node, recast) {
|
||||
return docgen.resolver.findExportedComponentDefinition(node, recast) ||
|
||||
docgen.resolver.findAllComponentDefinitions(node, recast)[0];
|
||||
}
|
||||
|
||||
function findExportedObject(ast, recast) {
|
||||
var objPath;
|
||||
let objPath;
|
||||
recast.visit(ast, {
|
||||
visitAssignmentExpression: function(path) {
|
||||
if (!objPath && docgen.utils.isExportsOrModuleAssignment(path)) {
|
||||
|
@ -93,9 +184,9 @@ function findExportedObject(ast, recast) {
|
|||
// handler.
|
||||
// This converts any expression, e.g. `foo` to an object expression of
|
||||
// the form `{propTypes: foo}`
|
||||
var b = recast.types.builders;
|
||||
var nt = recast.types.namedTypes;
|
||||
var obj = objPath.node;
|
||||
let b = recast.types.builders;
|
||||
let nt = recast.types.namedTypes;
|
||||
let obj = objPath.node;
|
||||
|
||||
// Hack: This is converting calls like
|
||||
//
|
||||
|
@ -107,7 +198,7 @@ function findExportedObject(ast, recast) {
|
|||
if (nt.CallExpression.check(obj) &&
|
||||
recast.print(obj.callee).code === 'Object.assign') {
|
||||
obj = objPath.node.arguments[1];
|
||||
var firstArg = objPath.node.arguments[0];
|
||||
let firstArg = objPath.node.arguments[0];
|
||||
if (recast.print(firstArg.callee).code === 'Object.create') {
|
||||
firstArg = firstArg.arguments[0];
|
||||
}
|
||||
|
@ -123,7 +214,29 @@ function findExportedObject(ast, recast) {
|
|||
return objPath;
|
||||
}
|
||||
|
||||
function findExportedType(ast, recast) {
|
||||
let types = recast.types.namedTypes;
|
||||
let definitions;
|
||||
recast.visit(ast, {
|
||||
visitExportNamedDeclaration: function(path) {
|
||||
if (path.node.declaration) {
|
||||
if (types.TypeAlias.check(path.node.declaration)) {
|
||||
if (!definitions) {
|
||||
definitions = [];
|
||||
}
|
||||
definitions.push(path);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return definitions;
|
||||
}
|
||||
|
||||
exports.stylePropTypeHandler = stylePropTypeHandler;
|
||||
exports.deprecatedPropTypeHandler = deprecatedPropTypeHandler;
|
||||
exports.typedefHandler = typedefHandler;
|
||||
exports.jsDocFormatHandler = jsDocFormatHandler;
|
||||
exports.findExportedOrFirst = findExportedOrFirst;
|
||||
exports.findExportedObject = findExportedObject;
|
||||
exports.findExportedType = findExportedType;
|
||||
|
|
|
@ -15,6 +15,9 @@ const fs = require('fs');
|
|||
const jsDocs = require('../jsdocs/jsdocs.js');
|
||||
const path = require('path');
|
||||
const slugify = require('../core/slugify');
|
||||
const babel = require('babel-core');
|
||||
const jsdocApi = require('jsdoc-api');
|
||||
const deepAssign = require('deep-assign');
|
||||
|
||||
const ANDROID_SUFFIX = 'android';
|
||||
const CROSS_SUFFIX = 'cross';
|
||||
|
@ -58,8 +61,8 @@ function getPlatformFromPath(filepath) {
|
|||
}
|
||||
|
||||
function getExamplePaths(componentName, componentPlatform) {
|
||||
var componentExample = '../Examples/UIExplorer/' + componentName + 'Example.';
|
||||
var pathsToCheck = [
|
||||
const componentExample = '../Examples/UIExplorer/' + componentName + 'Example.';
|
||||
let pathsToCheck = [
|
||||
componentExample + 'js',
|
||||
componentExample + componentPlatform + '.js',
|
||||
];
|
||||
|
@ -69,7 +72,7 @@ function getExamplePaths(componentName, componentPlatform) {
|
|||
componentExample + ANDROID_SUFFIX + '.js'
|
||||
);
|
||||
}
|
||||
var paths = [];
|
||||
let paths = [];
|
||||
pathsToCheck.map((p) => {
|
||||
if (fs.existsSync(p)) {
|
||||
paths.push(p);
|
||||
|
@ -79,12 +82,12 @@ function getExamplePaths(componentName, componentPlatform) {
|
|||
}
|
||||
|
||||
function getExamples(componentName, componentPlatform) {
|
||||
var paths = getExamplePaths(componentName, componentPlatform);
|
||||
const paths = getExamplePaths(componentName, componentPlatform);
|
||||
if (paths) {
|
||||
var examples = [];
|
||||
let examples = [];
|
||||
paths.map((p) => {
|
||||
var platform = p.match(/Example\.(.*)\.js$/);
|
||||
var title = '';
|
||||
const platform = p.match(/Example\.(.*)\.js$/);
|
||||
let title = '';
|
||||
if ((componentPlatform === CROSS_SUFFIX) && (platform !== null)) {
|
||||
title = platform[1].toUpperCase();
|
||||
}
|
||||
|
@ -128,7 +131,7 @@ function filterMethods(method) {
|
|||
// Determines whether a component should have a link to a runnable example
|
||||
|
||||
function isRunnable(componentName, componentPlatform) {
|
||||
var paths = getExamplePaths(componentName, componentPlatform);
|
||||
const paths = getExamplePaths(componentName, componentPlatform);
|
||||
if (paths && paths.length > 0) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -206,30 +209,229 @@ function componentsToMarkdown(type, json, filepath, idx, styles) {
|
|||
|
||||
let componentCount;
|
||||
|
||||
function getTypedef(filepath, fileContent, json) {
|
||||
let typedefDocgen;
|
||||
try {
|
||||
typedefDocgen = docgen.parse(
|
||||
fileContent,
|
||||
docgenHelpers.findExportedType,
|
||||
[docgenHelpers.typedefHandler]
|
||||
).map((type) => type.typedef);
|
||||
} catch (e) {
|
||||
// Ignore errors due to missing exported type definitions
|
||||
if (e.message.indexOf(docgen.ERROR_MISSING_DEFINITION) !== -1) {
|
||||
console.error('Cannot parse file', filepath, e);
|
||||
}
|
||||
}
|
||||
if (!json) {
|
||||
return typedefDocgen;
|
||||
}
|
||||
let typedef = typedefDocgen;
|
||||
if (json.typedef && json.typedef.length !== 0) {
|
||||
json.typedef.forEach(def => {
|
||||
const typedefMatch = typedefDocgen.find(t => t.name === def.name);
|
||||
if (typedefMatch) {
|
||||
typedef.name = Object.assign(typedefMatch, def);
|
||||
} else {
|
||||
typedef.push(def);
|
||||
}
|
||||
});
|
||||
}
|
||||
return typedef;
|
||||
}
|
||||
|
||||
function renderComponent(filepath) {
|
||||
const fileContent = fs.readFileSync(filepath);
|
||||
const json = docgen.parse(
|
||||
fs.readFileSync(filepath),
|
||||
fileContent,
|
||||
docgenHelpers.findExportedOrFirst,
|
||||
docgen.defaultHandlers.concat([
|
||||
docgenHelpers.stylePropTypeHandler,
|
||||
docgenHelpers.deprecatedPropTypeHandler,
|
||||
docgenHelpers.jsDocFormatHandler,
|
||||
])
|
||||
);
|
||||
json.typedef = getTypedef(filepath, fileContent);
|
||||
|
||||
return componentsToMarkdown('component', json, filepath, componentCount++, styleDocs);
|
||||
}
|
||||
|
||||
function renderAPI(type) {
|
||||
return function(filepath) {
|
||||
let json;
|
||||
try {
|
||||
json = jsDocs(fs.readFileSync(filepath).toString());
|
||||
} catch (e) {
|
||||
console.error('Cannot parse file', filepath, e);
|
||||
json = {};
|
||||
}
|
||||
return componentsToMarkdown(type, json, filepath, componentCount++);
|
||||
function isJsDocFormat(fileContent) {
|
||||
const reComment = /\/\*\*[\s\S]+?\*\//g;
|
||||
const comments = fileContent.match(reComment);
|
||||
if (!comments) {
|
||||
return false;
|
||||
}
|
||||
return !!comments[0].match(/\s*\*\s+@jsdoc/);
|
||||
}
|
||||
|
||||
function parseAPIJsDocFormat(filepath, fileContent) {
|
||||
const fileName = path.basename(filepath);
|
||||
const babelRC = {
|
||||
'filename': fileName,
|
||||
'sourceFileName': fileName,
|
||||
'plugins': [
|
||||
'transform-flow-strip-types',
|
||||
'babel-plugin-syntax-trailing-function-commas',
|
||||
]
|
||||
};
|
||||
// Babel transform
|
||||
const code = babel.transform(fileContent, babelRC).code;
|
||||
// Parse via jsdocs
|
||||
let jsonParsed = jsdocApi.explainSync({
|
||||
source: code,
|
||||
configure: './jsdocs/jsdoc-conf.json'
|
||||
});
|
||||
// Cleanup jsdocs return
|
||||
jsonParsed = jsonParsed.filter(i => {
|
||||
return !i.undocumented && !/package|file/.test(i.kind);
|
||||
});
|
||||
jsonParsed = jsonParsed.map((identifier) => {
|
||||
delete identifier.comment;
|
||||
return identifier;
|
||||
});
|
||||
jsonParsed.forEach((identifier, index) => {
|
||||
identifier.order = index;
|
||||
});
|
||||
// Group by "kind"
|
||||
let json = {};
|
||||
jsonParsed.forEach((identifier, index) => {
|
||||
let kind = identifier.kind;
|
||||
if (kind === 'function') {
|
||||
kind = 'methods';
|
||||
}
|
||||
if (!json[kind]) {
|
||||
json[kind] = [];
|
||||
}
|
||||
delete identifier.kind;
|
||||
json[kind].push(identifier);
|
||||
});
|
||||
json.typedef = getTypedef(filepath, fileContent, json);
|
||||
return json;
|
||||
}
|
||||
|
||||
function parseAPIInferred(filepath, fileContent) {
|
||||
let json;
|
||||
try {
|
||||
json = jsDocs(fileContent);
|
||||
} catch (e) {
|
||||
console.error('Cannot parse file', filepath, e);
|
||||
json = {};
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
function getTypeName(type) {
|
||||
let typeName;
|
||||
switch (type.name) {
|
||||
case 'signature':
|
||||
typeName = type.type;
|
||||
break;
|
||||
case 'union':
|
||||
typeName = type.value ?
|
||||
type.value.map(getTypeName) :
|
||||
type.elements.map(getTypeName);
|
||||
break;
|
||||
case 'enum':
|
||||
if (typeof type.value === 'string') {
|
||||
typeName = type.value;
|
||||
} else {
|
||||
typeName = 'enum';
|
||||
}
|
||||
break;
|
||||
case '$Enum':
|
||||
if (type.elements[0].signature.properties) {
|
||||
typeName = type.elements[0].signature.properties.map(p => p.key);
|
||||
}
|
||||
break;
|
||||
case 'arrayOf':
|
||||
typeName = getTypeName(type.value);
|
||||
break;
|
||||
case 'instanceOf':
|
||||
typeName = type.value;
|
||||
break;
|
||||
case 'func':
|
||||
typeName = 'function';
|
||||
break;
|
||||
default:
|
||||
typeName = type.alias ? type.alias : type.name;
|
||||
break;
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
function getTypehintRec(typehint) {
|
||||
if (typehint.type === 'simple') {
|
||||
return typehint.value;
|
||||
}
|
||||
if (typehint.type === 'generic') {
|
||||
return getTypehintRec(typehint.value[0]) +
|
||||
'<' + getTypehintRec(typehint.value[1]) + '>';
|
||||
}
|
||||
return JSON.stringify(typehint);
|
||||
}
|
||||
|
||||
function getTypehint(typehint) {
|
||||
if (typeof typehint === 'object' && typehint.name) {
|
||||
return getTypeName(typehint);
|
||||
}
|
||||
try {
|
||||
var typehint = JSON.parse(typehint);
|
||||
} catch (e) {
|
||||
return typehint.split('|').map(type => type.trim());
|
||||
}
|
||||
return getTypehintRec(typehint);
|
||||
}
|
||||
|
||||
function getJsDocFormatType(entities) {
|
||||
let modEntities = entities;
|
||||
if (entities) {
|
||||
if (typeof entities === 'object' && entities.length) {
|
||||
entities.map((entity, entityIndex) => {
|
||||
if (entity.typehint) {
|
||||
const typeNames = [].concat(getTypehint(entity.typehint));
|
||||
modEntities[entityIndex].type = { names: typeNames };
|
||||
delete modEntities[entityIndex].typehint;
|
||||
}
|
||||
if (entity.name) {
|
||||
const regexOptionalType = /\?$/;
|
||||
if (regexOptionalType.test(entity.name)) {
|
||||
modEntities[entityIndex].optional = true;
|
||||
modEntities[entityIndex].name =
|
||||
entity.name.replace(regexOptionalType, '');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const typeNames = [].concat(getTypehint(entities));
|
||||
return { type: { names : typeNames } };
|
||||
}
|
||||
}
|
||||
return modEntities;
|
||||
}
|
||||
|
||||
function renderAPI(filepath, type) {
|
||||
const fileContent = fs.readFileSync(filepath).toString();
|
||||
let json = parseAPIInferred(filepath, fileContent);
|
||||
if (isJsDocFormat(fileContent)) {
|
||||
let jsonJsDoc = parseAPIJsDocFormat(filepath, fileContent);
|
||||
// Combine method info with jsdoc fomatted content
|
||||
const methods = json.methods;
|
||||
if (methods && methods.length) {
|
||||
let modMethods = methods;
|
||||
methods.map((method, methodIndex) => {
|
||||
modMethods[methodIndex].params = getJsDocFormatType(method.params);
|
||||
modMethods[methodIndex].returns =
|
||||
getJsDocFormatType(method.returntypehint);
|
||||
delete modMethods[methodIndex].returntypehint;
|
||||
});
|
||||
json.methods = modMethods;
|
||||
// Use deep Object.assign so duplicate properties are overwritten.
|
||||
deepAssign(jsonJsDoc.methods, json.methods);
|
||||
}
|
||||
json = jsonJsDoc;
|
||||
}
|
||||
return componentsToMarkdown(type, json, filepath, componentCount++);
|
||||
}
|
||||
|
||||
function renderStyle(filepath) {
|
||||
|
@ -355,8 +557,12 @@ module.exports = function() {
|
|||
componentCount = 0;
|
||||
return [].concat(
|
||||
components.map(renderComponent),
|
||||
apis.map(renderAPI('api')),
|
||||
apis.map((filepath) => {
|
||||
return renderAPI(filepath, 'api');
|
||||
}),
|
||||
stylesWithPermalink.map(renderStyle),
|
||||
polyfills.map(renderAPI('Polyfill'))
|
||||
polyfills.map((filepath) => {
|
||||
return renderAPI(filepath, 'Polyfill');
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1512,3 +1512,53 @@ input#algolia-doc-search:focus {
|
|||
background-color: hsl(198, 100%, 96%);
|
||||
color: #3B3738;
|
||||
}
|
||||
|
||||
.params, .props
|
||||
{
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.params .name, .props .name, .name code {
|
||||
color: #4D4E53;
|
||||
}
|
||||
|
||||
.params td, .params th, .props td, .props th
|
||||
{
|
||||
border: 1px solid #ddd;
|
||||
margin: 0px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
padding: 4px 6px;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.params thead tr, .props thead tr
|
||||
{
|
||||
background-color: hsl(198, 75%, 88%);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.params .params thead tr, .props .props thead tr
|
||||
{
|
||||
background-color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.params th, .props th { border-right: 1px solid #aaa; }
|
||||
.params thead .last, .props thead .last { border-right: 1px solid #ddd; }
|
||||
|
||||
.params td.description > div > p:first-child,
|
||||
.props td.description > div > p:first-child
|
||||
{
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.params td.description > p:last-child,
|
||||
.props td.description > p:last-child
|
||||
{
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue