diff options
Diffstat (limited to 'packages/react-docs')
-rw-r--r-- | packages/react-docs/README.md | 6 | ||||
-rw-r--r-- | packages/react-docs/package.json | 10 | ||||
-rw-r--r-- | packages/react-docs/src/components/documentation.tsx | 66 | ||||
-rw-r--r-- | packages/react-docs/src/components/interface.tsx | 23 | ||||
-rw-r--r-- | packages/react-docs/src/components/property_block.tsx | 77 | ||||
-rw-r--r-- | packages/react-docs/src/components/signature.tsx | 3 | ||||
-rw-r--r-- | packages/react-docs/src/components/type.tsx | 86 | ||||
-rw-r--r-- | packages/react-docs/src/components/type_definition.tsx | 17 | ||||
-rw-r--r-- | packages/react-docs/src/docs_info.ts | 71 | ||||
-rw-r--r-- | packages/react-docs/src/index.ts | 10 | ||||
-rw-r--r-- | packages/react-docs/src/monorepo_scripts/postpublish.ts | 8 | ||||
-rw-r--r-- | packages/react-docs/src/types.ts | 29 | ||||
-rw-r--r-- | packages/react-docs/src/utils/typedoc_utils.ts | 251 |
13 files changed, 423 insertions, 234 deletions
diff --git a/packages/react-docs/README.md b/packages/react-docs/README.md index c0efc4a0b..5d56207de 100644 --- a/packages/react-docs/README.md +++ b/packages/react-docs/README.md @@ -1,6 +1,8 @@ ## @0xproject/react-docs -A full-page React component for rendering beautiful documentation for Solidity and Typescript code generated with [TypeDoc](http://typedoc.org/) or [Doxity](https://github.com/0xproject/doxity). See a [live example](http://react-docs-example.s3-website-us-east-1.amazonaws.com/). +#### WARNING: Alpha software. Expect things to break when trying to use. + +A full-page React component for rendering beautiful documentation for Solidity and Typescript code generated with [TypeDoc](http://typedoc.org/) or [Doxity](https://github.com/0xproject/doxity). <div style="text-align: center;"> <img src="https://s3.eu-west-2.amazonaws.com/0x-wiki-images/screenshot.png" style="padding-bottom: 20px; padding-top: 20px;" width="80%" /> @@ -27,8 +29,6 @@ yarn add @0xproject/react-docs ## Usage -View the [live example](http://react-docs-example.s3-website-us-east-1.amazonaws.com/) that renders the [@0xproject/web3-wrapper](https://github.com/0xProject/0x-monorepo/tree/development/packages/web3-wrapper) Typescript package. It's source code is in the [react-docs-example](https://github.com/0xProject/0x-monorepo/tree/development/packages/react-docs-example) 0x monorepo subpackage. - This package exposes both a single `Documentation` react component that will render a docs page, as well as all of it's sub-components in case someone wants to build their own layout. Currently this package still has some external dependencies outside of the `Documentation` component, so please start your project off by copying the [react-docs-example](https://github.com/0xProject/0x-monorepo/tree/development/packages/react-docs-example) directory and modifying it there. If you need changes in the [react-docs](https://github.com/0xProject/0x-monorepo/tree/development/packages/react-docs) package, fork the 0x monorepo, make the required changes and submit a PR. Until we merge it, you can have your project depend on your own custom fork. diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index e24ed8e6f..3e2ca6794 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -9,10 +9,9 @@ "types": "lib/index.d.ts", "scripts": { "lint": "tslint --project .", - "build": "tsc && copyfiles -u 2 './lib/monorepo_scripts/**/*' ./scripts", + "build": "tsc", "watch_without_deps": "tsc -w", - "clean": "shx rm -rf lib scripts", - "manual:postpublish": "yarn build; node ./scripts/postpublish.js" + "clean": "shx rm -rf lib" }, "author": "Fabio Berger", "license": "Apache-2.0", @@ -26,7 +25,6 @@ }, "devDependencies": { "@0xproject/dev-utils": "^1.0.4", - "@0xproject/monorepo-scripts": "^1.0.5", "@0xproject/tslint-config": "^1.0.5", "@types/compare-versions": "^3.0.0", "copyfiles": "^1.2.0", @@ -43,7 +41,7 @@ "@types/node": "^8.0.53", "@types/react": "*", "@types/react-dom": "*", - "@types/react-scroll": "0.0.31", + "@types/react-scroll": "1.5.3", "basscss": "^8.0.3", "compare-versions": "^3.0.1", "lodash": "^4.17.5", @@ -51,7 +49,7 @@ "react": "15.6.1", "react-dom": "15.6.1", "react-markdown": "^3.2.2", - "react-scroll": "^1.5.2", + "react-scroll": "0xproject/react-scroll#similar-to-pr-330", "react-tooltip": "^3.2.7", "semver": "5.5.0" }, diff --git a/packages/react-docs/src/components/documentation.tsx b/packages/react-docs/src/components/documentation.tsx index ff33220d2..f4f1d2aa9 100644 --- a/packages/react-docs/src/components/documentation.tsx +++ b/packages/react-docs/src/components/documentation.tsx @@ -26,14 +26,12 @@ import { TypescriptFunction, TypescriptMethod, } from '../types'; -import { constants } from '../utils/constants'; import { Badge } from './badge'; import { Comment } from './comment'; import { EventDefinition } from './event_definition'; import { SignatureBlock } from './signature_block'; -import { SourceLink } from './source_link'; -import { Type } from './type'; +import { PropertyBlock } from './property_block'; import { TypeDefinition } from './type_definition'; const networkNameToColor: { [network: string]: string } = { @@ -129,7 +127,7 @@ export class Documentation extends React.Component<DocumentationProps, Documenta selectedVersion={this.props.selectedVersion} versions={this.props.availableVersions} sidebarHeader={this.props.sidebarHeader} - topLevelMenu={this.props.docsInfo.getMenu(this.props.selectedVersion)} + topLevelMenu={this.props.docsInfo.menu} menuSubsectionsBySection={menuSubsectionsBySection} onVersionSelected={this.props.onVersionSelected} /> @@ -172,7 +170,7 @@ export class Documentation extends React.Component<DocumentationProps, Documenta ); } private _renderDocumentation(): React.ReactNode { - const subMenus = _.values(this.props.docsInfo.getMenu()); + const subMenus = _.values(this.props.docsInfo.menu); const orderedSectionNames = _.flatten(subMenus); const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.props.docAgnosticFormat); @@ -210,6 +208,14 @@ export class Documentation extends React.Component<DocumentationProps, Documenta return null; } + const isExportedFunctionSection = + docSection.functions.length === 1 && + _.isEmpty(docSection.types) && + _.isEmpty(docSection.methods) && + _.isEmpty(docSection.constructors) && + _.isEmpty(docSection.properties) && + _.isEmpty(docSection.events); + const sortedTypes = _.sortBy(docSection.types, 'name'); const typeDefs = _.map(sortedTypes, customType => { return ( @@ -218,12 +224,16 @@ export class Documentation extends React.Component<DocumentationProps, Documenta key={`type-${customType.name}`} customType={customType} docsInfo={this.props.docsInfo} + typeDefinitionByName={typeDefinitionByName} /> ); }); const sortedProperties = _.sortBy(docSection.properties, 'name'); - const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName)); + const propertyDefs = _.map( + sortedProperties, + this._renderProperty.bind(this, sectionName, typeDefinitionByName), + ); const sortedMethods = _.sortBy(docSection.methods, 'name'); const methodDefs = _.map(sortedMethods, method => { @@ -258,13 +268,12 @@ export class Documentation extends React.Component<DocumentationProps, Documenta {this._renderNetworkBadgesIfExists(sectionName)} </div> {docSection.comment && <Comment comment={docSection.comment} />} - {!_.isEmpty(docSection.constructors) && - this.props.docsInfo.isVisibleConstructor(sectionName) && ( - <div> - <h2 style={headerStyle}>Constructor</h2> - {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)} - </div> - )} + {!_.isEmpty(docSection.constructors) && ( + <div> + <h2 style={headerStyle}>Constructor</h2> + {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)} + </div> + )} {!_.isEmpty(docSection.properties) && ( <div> <h2 style={headerStyle}>Properties</h2> @@ -279,7 +288,7 @@ export class Documentation extends React.Component<DocumentationProps, Documenta )} {!_.isEmpty(docSection.functions) && ( <div> - <h2 style={headerStyle}>Functions</h2> + {!isExportedFunctionSection && <h2 style={headerStyle}>Functions</h2>} <div>{functionDefs}</div> </div> )} @@ -343,22 +352,21 @@ export class Documentation extends React.Component<DocumentationProps, Documenta }); return <div>{constructorDefs}</div>; } - private _renderProperty(sectionName: string, property: Property): React.ReactNode { + private _renderProperty( + sectionName: string, + typeDefinitionByName: TypeDefinitionByName, + property: Property, + ): React.ReactNode { return ( - <div key={`property-${property.name}-${property.type.name}`} className="pb3"> - <code className={`hljs ${constants.TYPE_TO_SYNTAX[this.props.docsInfo.type]}`}> - {property.name}:{' '} - <Type type={property.type} sectionName={sectionName} docsInfo={this.props.docsInfo} /> - </code> - {property.source && ( - <SourceLink - version={this.props.selectedVersion} - source={property.source} - sourceUrl={this.props.sourceUrl} - /> - )} - {property.comment && <Comment comment={property.comment} className="py2" />} - </div> + <PropertyBlock + key={`property-${property.name}-${property.type.name}`} + property={property} + sectionName={sectionName} + docsInfo={this.props.docsInfo} + sourceUrl={this.props.sourceUrl} + selectedVersion={this.props.selectedVersion} + typeDefinitionByName={typeDefinitionByName} + /> ); } private _renderSignatureBlocks( diff --git a/packages/react-docs/src/components/interface.tsx b/packages/react-docs/src/components/interface.tsx index a881c7fec..eaf57ce93 100644 --- a/packages/react-docs/src/components/interface.tsx +++ b/packages/react-docs/src/components/interface.tsx @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { DocsInfo } from '../docs_info'; -import { CustomType, TypeDocTypes } from '../types'; +import { CustomType, TypeDefinitionByName } from '../types'; import { Signature } from './signature'; import { Type } from './type'; @@ -11,6 +11,7 @@ export interface InterfaceProps { type: CustomType; sectionName: string; docsInfo: DocsInfo; + typeDefinitionByName: TypeDefinitionByName; } export const Interface = (props: InterfaceProps) => { @@ -19,9 +20,7 @@ export const Interface = (props: InterfaceProps) => { return ( <span key={`property-${property.name}-${property.type}-${type.name}`}> {property.name}:{' '} - {property.type && property.type.typeDocType !== TypeDocTypes.Reflection ? ( - <Type type={property.type} sectionName={props.sectionName} docsInfo={props.docsInfo} /> - ) : ( + {property.type && !_.isUndefined(property.type.method) ? ( <Signature name={property.type.method.name} returnType={property.type.method.returnType} @@ -31,6 +30,14 @@ export const Interface = (props: InterfaceProps) => { shouldHideMethodName={true} shouldUseArrowSyntax={true} docsInfo={props.docsInfo} + typeDefinitionByName={props.typeDefinitionByName} + /> + ) : ( + <Type + type={property.type} + sectionName={props.sectionName} + docsInfo={props.docsInfo} + typeDefinitionByName={props.typeDefinitionByName} /> )}, </span> @@ -41,7 +48,13 @@ export const Interface = (props: InterfaceProps) => { const is = type.indexSignature; const param = ( <span key={`indexSigParams-${is.keyName}-${is.keyType}-${type.name}`}> - {is.keyName}: <Type type={is.keyType} sectionName={props.sectionName} docsInfo={props.docsInfo} /> + {is.keyName}:{' '} + <Type + type={is.keyType} + sectionName={props.sectionName} + docsInfo={props.docsInfo} + typeDefinitionByName={props.typeDefinitionByName} + /> </span> ); properties.push( diff --git a/packages/react-docs/src/components/property_block.tsx b/packages/react-docs/src/components/property_block.tsx new file mode 100644 index 000000000..6e5c451be --- /dev/null +++ b/packages/react-docs/src/components/property_block.tsx @@ -0,0 +1,77 @@ +import { AnchorTitle, HeaderSizes } from '@0xproject/react-shared'; +import * as React from 'react'; + +import { DocsInfo } from '../docs_info'; +import { Property, TypeDefinitionByName } from '../types'; +import { constants } from '../utils/constants'; + +import { Comment } from './comment'; +import { Type } from './type'; +import { SourceLink } from './source_link'; + +export interface PropertyBlockProps { + property: Property; + sectionName: string; + docsInfo: DocsInfo; + sourceUrl: string; + selectedVersion: string; + typeDefinitionByName: TypeDefinitionByName; +} + +export interface PropertyBlockState { + shouldShowAnchor: boolean; +} + +export class PropertyBlock extends React.Component<PropertyBlockProps, PropertyBlockState> { + constructor(props: PropertyBlockProps) { + super(props); + this.state = { + shouldShowAnchor: false, + }; + } + public render(): React.ReactNode { + const property = this.props.property; + const sectionName = this.props.sectionName; + return ( + <div + id={`${this.props.sectionName}-${property.name}`} + className="pb4 pt2" + key={`property-${property.name}-${property.type.name}`} + onMouseOver={this._setAnchorVisibility.bind(this, true)} + onMouseOut={this._setAnchorVisibility.bind(this, false)} + > + <div className="pb2" style={{ lineHeight: 1.3 }}> + <AnchorTitle + headerSize={HeaderSizes.H3} + title={property.name} + id={`${sectionName}-${property.name}`} + shouldShowAnchor={this.state.shouldShowAnchor} + /> + </div> + <code className={`hljs ${constants.TYPE_TO_SYNTAX[this.props.docsInfo.type]}`}> + {(property as any).callPath} + {property.name}:{' '} + <Type + type={property.type} + sectionName={sectionName} + docsInfo={this.props.docsInfo} + typeDefinitionByName={this.props.typeDefinitionByName} + /> + </code> + {property.source && ( + <SourceLink + version={this.props.selectedVersion} + source={property.source} + sourceUrl={this.props.sourceUrl} + /> + )} + {property.comment && <Comment comment={property.comment} className="py2" />} + </div> + ); + } + private _setAnchorVisibility(shouldShowAnchor: boolean): void { + this.setState({ + shouldShowAnchor, + }); + } +} diff --git a/packages/react-docs/src/components/signature.tsx b/packages/react-docs/src/components/signature.tsx index 77e9cc909..d9567c9f6 100644 --- a/packages/react-docs/src/components/signature.tsx +++ b/packages/react-docs/src/components/signature.tsx @@ -3,7 +3,6 @@ import * as React from 'react'; import { DocsInfo } from '../docs_info'; import { Parameter, Type as TypeDef, TypeDefinitionByName, TypeParameter } from '../types'; -import { constants } from '../utils/constants'; import { Type } from './type'; @@ -27,7 +26,7 @@ const defaultProps = { }; export const Signature: React.SFC<SignatureProps> = (props: SignatureProps) => { - const sectionName = constants.TYPES_SECTION_NAME; + const sectionName = props.sectionName; const parameters = renderParameters(props.parameters, props.docsInfo, sectionName, props.typeDefinitionByName); const paramStringArray: any[] = []; // HACK: For now we don't put params on newlines if there are less then 2 of them. diff --git a/packages/react-docs/src/components/type.tsx b/packages/react-docs/src/components/type.tsx index e453349ef..1c580caab 100644 --- a/packages/react-docs/src/components/type.tsx +++ b/packages/react-docs/src/components/type.tsx @@ -9,8 +9,11 @@ import { DocsInfo } from '../docs_info'; import { Type as TypeDef, TypeDefinitionByName, TypeDocTypes } from '../types'; import { Signature } from './signature'; +import { constants } from '../utils/constants'; import { TypeDefinition } from './type_definition'; +const basicJsTypes = ['string', 'number', 'undefined', 'null', 'boolean']; + export interface TypeProps { type: TypeDef; docsInfo: DocsInfo; @@ -43,7 +46,7 @@ export function Type(props: TypeProps): any { <span> <Type key={key} - type={arg.elementType} + type={arg} sectionName={props.sectionName} typeDefinitionByName={props.typeDefinitionByName} docsInfo={props.docsInfo} @@ -72,6 +75,9 @@ export function Type(props: TypeProps): any { case TypeDocTypes.Array: typeName = type.elementType.name; + if (_.includes(basicJsTypes, typeName)) { + typeNameColor = colors.orange; + } break; case TypeDocTypes.Union: @@ -92,19 +98,43 @@ export function Type(props: TypeProps): any { break; case TypeDocTypes.Reflection: - typeName = ( - <Signature - name={type.method.name} - returnType={type.method.returnType} - parameters={type.method.parameters} - typeParameter={type.method.typeParameter} - sectionName={props.sectionName} - shouldHideMethodName={true} - shouldUseArrowSyntax={true} - docsInfo={props.docsInfo} - typeDefinitionByName={props.typeDefinitionByName} - /> - ); + if (!_.isUndefined(type.method)) { + typeName = ( + <Signature + name={type.method.name} + returnType={type.method.returnType} + parameters={type.method.parameters} + typeParameter={type.method.typeParameter} + sectionName={props.sectionName} + shouldHideMethodName={true} + shouldUseArrowSyntax={true} + docsInfo={props.docsInfo} + typeDefinitionByName={props.typeDefinitionByName} + /> + ); + } else if (!_.isUndefined(type.indexSignature)) { + const is = type.indexSignature; + const param = ( + <span key={`indexSigParams-${is.keyName}-${is.keyType}-${type.name}`}> + {is.keyName}:{' '} + <Type + type={is.keyType} + sectionName={props.sectionName} + docsInfo={props.docsInfo} + typeDefinitionByName={props.typeDefinitionByName} + /> + </span> + ); + typeName = ( + <span key={`indexSignature-${type.name}-${is.keyType.name}`}> + {'{'}[{param}]: {is.valueName} + {'}'} + </span> + ); + } else { + throw new Error(`Unrecognized Reflection type that isn't a Method nor an Index Signature`); + } + break; case TypeDocTypes.TypeParameter: @@ -142,7 +172,6 @@ export function Type(props: TypeProps): any { let typeNameUrlIfExists; let typePrefixIfExists; - let sectionNameIfExists; if (!_.isUndefined(props.docsInfo.typeConfigs)) { typeNameUrlIfExists = !_.isUndefined(props.docsInfo.typeConfigs.typeNameToExternalLink) ? props.docsInfo.typeConfigs.typeNameToExternalLink[typeName as string] @@ -150,9 +179,6 @@ export function Type(props: TypeProps): any { typePrefixIfExists = !_.isUndefined(props.docsInfo.typeConfigs.typeNameToPrefix) ? props.docsInfo.typeConfigs.typeNameToPrefix[typeName as string] : undefined; - sectionNameIfExists = !_.isUndefined(props.docsInfo.typeConfigs.typeNameToDocSection) - ? props.docsInfo.typeConfigs.typeNameToDocSection[typeName as string] - : undefined; } if (!_.isUndefined(typeNameUrlIfExists)) { typeName = ( @@ -168,35 +194,26 @@ export function Type(props: TypeProps): any { ); } else if ( (isReference || isArray) && - (props.docsInfo.isPublicType(typeName as string) || !_.isUndefined(sectionNameIfExists)) + props.typeDefinitionByName && + props.typeDefinitionByName[typeName as string] ) { const id = Math.random().toString(); - const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists) - ? `${props.sectionName}-${typeName}` - : sectionNameIfExists; - let typeDefinition; - if (props.typeDefinitionByName) { - typeDefinition = props.typeDefinitionByName[typeName as string]; - } + const typeDefinitionAnchorId = `${constants.TYPES_SECTION_NAME}-${typeName}`; + let typeDefinition = props.typeDefinitionByName[typeName as string]; typeName = ( <ScrollLink to={typeDefinitionAnchorId} offset={0} + hashSpy={true} duration={sharedConstants.DOCS_SCROLL_DURATION_MS} containerId={sharedConstants.DOCS_CONTAINER_ID} > - {_.isUndefined(typeDefinition) || sharedUtils.isUserOnMobile() ? ( - <span - onClick={sharedUtils.setUrlHash.bind(null, typeDefinitionAnchorId)} - style={{ color: colors.lightBlueA700, cursor: 'pointer' }} - > - {typeName} - </span> + {sharedUtils.isUserOnMobile() ? ( + <span style={{ color: colors.lightBlueA700, cursor: 'pointer' }}>{typeName}</span> ) : ( <span data-tip={true} data-for={id} - onClick={sharedUtils.setUrlHash.bind(null, typeDefinitionAnchorId)} style={{ color: colors.lightBlueA700, cursor: 'pointer', @@ -210,6 +227,7 @@ export function Type(props: TypeProps): any { customType={typeDefinition} shouldAddId={false} docsInfo={props.docsInfo} + typeDefinitionByName={props.typeDefinitionByName} /> </ReactTooltip> </span> diff --git a/packages/react-docs/src/components/type_definition.tsx b/packages/react-docs/src/components/type_definition.tsx index c4bd7359a..775d9890f 100644 --- a/packages/react-docs/src/components/type_definition.tsx +++ b/packages/react-docs/src/components/type_definition.tsx @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import * as React from 'react'; import { DocsInfo } from '../docs_info'; -import { CustomType, CustomTypeChild, KindString, TypeDocTypes } from '../types'; +import { CustomType, CustomTypeChild, KindString, TypeDocTypes, TypeDefinitionByName } from '../types'; import { constants } from '../utils/constants'; import { Comment } from './comment'; @@ -19,6 +19,7 @@ export interface TypeDefinitionProps { customType: CustomType; shouldAddId?: boolean; docsInfo: DocsInfo; + typeDefinitionByName?: TypeDefinitionByName; } export interface TypeDefinitionState { @@ -37,9 +38,6 @@ export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDef } public render(): React.ReactNode { const customType = this.props.customType; - if (!this.props.docsInfo.isPublicType(customType.name)) { - return null; // no-op - } let typePrefix: string; let codeSnippet: React.ReactNode; @@ -47,7 +45,12 @@ export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDef case KindString.Interface: typePrefix = 'Interface'; codeSnippet = ( - <Interface type={customType} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} /> + <Interface + type={customType} + sectionName={this.props.sectionName} + docsInfo={this.props.docsInfo} + typeDefinitionByName={this.props.typeDefinitionByName} + /> ); break; @@ -77,6 +80,7 @@ export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDef type={customType.type} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} + typeDefinitionByName={this.props.typeDefinitionByName} /> ) : ( <Signature @@ -89,6 +93,7 @@ export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDef shouldHideMethodName={true} shouldUseArrowSyntax={true} docsInfo={this.props.docsInfo} + typeDefinitionByName={this.props.typeDefinitionByName} /> )} </span> @@ -103,7 +108,7 @@ export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDef return ( <div id={this.props.shouldAddId ? typeDefinitionAnchorId : ''} - className="pb2" + className="pb2 pt2" style={{ overflow: 'hidden', width: '100%' }} onMouseOver={this._setAnchorVisibility.bind(this, true)} onMouseOut={this._setAnchorVisibility.bind(this, false)} diff --git a/packages/react-docs/src/docs_info.ts b/packages/react-docs/src/docs_info.ts index b37942da6..4267d8a98 100644 --- a/packages/react-docs/src/docs_info.ts +++ b/packages/react-docs/src/docs_info.ts @@ -13,7 +13,7 @@ import { SectionsMap, SupportedDocJson, TypeDefinitionByName, - TypeDocNode, + GeneratedDocJson, } from './types'; import { doxityUtils } from './utils/doxity_utils'; import { typeDocUtils } from './utils/typedoc_utils'; @@ -28,50 +28,16 @@ export class DocsInfo { public sectionNameToMarkdownByVersion: SectionNameToMarkdownByVersion; public contractsByVersionByNetworkId?: ContractsByVersionByNetworkId; public typeConfigs: DocsInfoTypeConfigs; - private readonly _docsInfo: DocsInfoConfig; constructor(config: DocsInfoConfig) { this.id = config.id; this.type = config.type; + this.menu = config.markdownMenu; this.displayName = config.displayName; this.packageUrl = config.packageUrl; - this.sections = config.sections; + this.sections = config.markdownSections; this.sectionNameToMarkdownByVersion = config.sectionNameToMarkdownByVersion; this.contractsByVersionByNetworkId = config.contractsByVersionByNetworkId; this.typeConfigs = config.typeConfigs; - this._docsInfo = config; - } - public isPublicType(typeName: string): boolean { - if (_.isUndefined(this._docsInfo.typeConfigs.publicTypes)) { - return false; - } - const isPublic = _.includes(this._docsInfo.typeConfigs.publicTypes, typeName); - return isPublic; - } - public getModulePathsIfExists(sectionName: string): string[] { - const modulePathsIfExists = this._docsInfo.sectionNameToModulePath[sectionName]; - return modulePathsIfExists; - } - public getMenu(selectedVersion?: string): { [section: string]: string[] } { - if (_.isUndefined(selectedVersion) || _.isUndefined(this._docsInfo.menuSubsectionToVersionWhenIntroduced)) { - return this._docsInfo.menu; - } - - const finalMenu = _.cloneDeep(this._docsInfo.menu); - if (_.isUndefined(finalMenu.contracts)) { - return finalMenu; - } - - // TODO: refactor to include more sections then simply the `contracts` section - finalMenu.contracts = _.filter(finalMenu.contracts, (contractName: string) => { - const versionIntroducedIfExists = this._docsInfo.menuSubsectionToVersionWhenIntroduced[contractName]; - if (!_.isUndefined(versionIntroducedIfExists)) { - const doesExistInSelectedVersion = compareVersions(selectedVersion, versionIntroducedIfExists) >= 0; - return doesExistInSelectedVersion; - } else { - return true; - } - }); - return finalMenu; } public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection { const menuSubsectionsBySection = {} as MenuSubsectionsBySection; @@ -86,22 +52,38 @@ export class DocsInfo { return; // no-op } + const isExportedFunctionSection = + docSection.functions.length === 1 && + _.isEmpty(docSection.types) && + _.isEmpty(docSection.methods) && + _.isEmpty(docSection.constructors) && + _.isEmpty(docSection.properties) && + _.isEmpty(docSection.events); + if (!_.isUndefined(this.sections.types) && sectionName === this.sections.types) { const sortedTypesNames = _.sortBy(docSection.types, 'name'); const typeNames = _.map(sortedTypesNames, t => t.name); menuSubsectionsBySection[sectionName] = typeNames; + } else if (isExportedFunctionSection) { + // Noop so that we don't have the method listed underneath itself. } else { let eventNames: string[] = []; if (!_.isUndefined(docSection.events)) { const sortedEventNames = _.sortBy(docSection.events, 'name'); eventNames = _.map(sortedEventNames, m => m.name); } - const sortedMethodNames = _.sortBy(docSection.methods, 'name'); - const methodNames = _.map(sortedMethodNames, m => m.name); - menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames]; + const propertiesSortedByName = _.sortBy(docSection.properties, 'name'); + const propertyNames = _.map(propertiesSortedByName, m => m.name); + const methodsSortedByName = _.sortBy(docSection.methods, 'name'); + const methodNames = _.map(methodsSortedByName, m => m.name); const sortedFunctionNames = _.sortBy(docSection.functions, 'name'); const functionNames = _.map(sortedFunctionNames, m => m.name); - menuSubsectionsBySection[sectionName] = [...eventNames, ...functionNames, ...methodNames]; + menuSubsectionsBySection[sectionName] = [ + ...eventNames, + ...propertyNames, + ...functionNames, + ...methodNames, + ]; } }); return menuSubsectionsBySection; @@ -115,14 +97,11 @@ export class DocsInfo { const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name') as any; return typeDefinitionByName; } - public isVisibleConstructor(sectionName: string): boolean { - return _.includes(this._docsInfo.visibleConstructors, sectionName); - } - public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat { + public convertToDocAgnosticFormat(docObj: DoxityDocObj | GeneratedDocJson): DocAgnosticFormat { if (this.type === SupportedDocJson.Doxity) { return doxityUtils.convertToDocAgnosticFormat(docObj as DoxityDocObj); } else { - return typeDocUtils.convertToDocAgnosticFormat(docObj as TypeDocNode, this); + return typeDocUtils.convertToDocAgnosticFormat(docObj as GeneratedDocJson, this); } } } diff --git a/packages/react-docs/src/index.ts b/packages/react-docs/src/index.ts index 30f5011b7..e4424f679 100644 --- a/packages/react-docs/src/index.ts +++ b/packages/react-docs/src/index.ts @@ -15,6 +15,14 @@ export { Type } from './components/type'; export { DocsInfo } from './docs_info'; -export { DocsInfoConfig, DocAgnosticFormat, DoxityDocObj, DocsMenu, SupportedDocJson, TypeDocNode } from './types'; +export { + DocsInfoConfig, + DocAgnosticFormat, + DoxityDocObj, + DocsMenu, + SupportedDocJson, + TypeDocNode, + GeneratedDocJson, +} from './types'; export { constants } from './utils/constants'; diff --git a/packages/react-docs/src/monorepo_scripts/postpublish.ts b/packages/react-docs/src/monorepo_scripts/postpublish.ts deleted file mode 100644 index dcb99d0f7..000000000 --- a/packages/react-docs/src/monorepo_scripts/postpublish.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { postpublishUtils } from '@0xproject/monorepo-scripts'; - -import * as packageJSON from '../package.json'; -import * as tsConfigJSON from '../tsconfig.json'; - -const cwd = `${__dirname}/..`; -// tslint:disable-next-line:no-floating-promises -postpublishUtils.runAsync(packageJSON, tsConfigJSON, cwd); diff --git a/packages/react-docs/src/types.ts b/packages/react-docs/src/types.ts index cbc774c2e..18c386a2b 100644 --- a/packages/react-docs/src/types.ts +++ b/packages/react-docs/src/types.ts @@ -7,21 +7,16 @@ export interface DocsInfoConfig { type: SupportedDocJson; displayName: string; packageUrl: string; - menu: DocsMenu; - sections: SectionsMap; + markdownMenu: DocsMenu; + markdownSections: SectionsMap; sectionNameToMarkdownByVersion: SectionNameToMarkdownByVersion; - visibleConstructors: string[]; - sectionNameToModulePath?: { [sectionName: string]: string[] }; - menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string }; contractsByVersionByNetworkId?: ContractsByVersionByNetworkId; typeConfigs?: DocsInfoTypeConfigs; } export interface DocsInfoTypeConfigs { typeNameToExternalLink?: { [typeName: string]: string }; - publicTypes?: string[]; typeNameToPrefix?: { [typeName: string]: string }; - typeNameToDocSection?: { [typeName: string]: string }; } export interface DocsMenu { @@ -40,6 +35,7 @@ export interface TypeDocType { typeArguments?: TypeDocType[]; declaration: TypeDocNode; elementType?: TypeDocType; + indexSignature?: TypeDocNode; } export interface TypeDocFlags { @@ -69,7 +65,7 @@ export interface TypeDocNode { returns?: string; declaration: TypeDocNode; flags?: TypeDocFlags; - indexSignature?: TypeDocNode | TypeDocNode[]; // TypeDocNode in TypeDoc <V0.9.0, TypeDocNode[] in >V0.9.0 + indexSignature?: TypeDocNode; signatures?: TypeDocNode[]; parameters?: TypeDocNode[]; typeParameter?: TypeDocNode[]; @@ -128,6 +124,7 @@ export interface TypescriptMethod extends BaseMethod { export interface TypescriptFunction extends BaseFunction { source?: Source; typeParameter?: TypeParameter; + callPath: string; } export interface SolidityMethod extends BaseMethod { @@ -161,6 +158,7 @@ export interface Type { elementType?: ElementType; types?: Type[]; method?: TypescriptMethod; + indexSignature?: IndexSignature; } export interface ElementType { @@ -207,6 +205,7 @@ export interface Property { type: Type; source?: Source; comment?: string; + callPath?: string; } export interface BaseMethod { @@ -292,3 +291,17 @@ export enum AbiTypes { Function = 'function', Event = 'event', } + +export interface ExportNameToTypedocNames { + [exportName: string]: string[]; +} + +export interface Metadata { + exportPathToTypedocNames: ExportNameToTypedocNames; + exportPathOrder: string[]; +} + +export interface GeneratedDocJson { + metadata: Metadata; + typedocJson: TypeDocNode; +} diff --git a/packages/react-docs/src/utils/typedoc_utils.ts b/packages/react-docs/src/utils/typedoc_utils.ts index a6d938e94..b45dc73a8 100644 --- a/packages/react-docs/src/utils/typedoc_utils.ts +++ b/packages/react-docs/src/utils/typedoc_utils.ts @@ -19,8 +19,11 @@ import { TypeParameter, TypescriptFunction, TypescriptMethod, + GeneratedDocJson, } from '../types'; +import { constants } from './constants'; + export const typeDocUtils = { isType(entity: TypeDocNode): boolean { return ( @@ -55,65 +58,89 @@ export const typeDocUtils = { }); return moduleDefinitions; }, - convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat { - const subMenus = _.values(docsInfo.getMenu()); - const orderedSectionNames = _.flatten(subMenus); + convertToDocAgnosticFormat(generatedDocJson: GeneratedDocJson, docsInfo: DocsInfo): DocAgnosticFormat { + const exportPathOrder = generatedDocJson.metadata.exportPathOrder; + const exportPathToTypedocNames = generatedDocJson.metadata.exportPathToTypedocNames; + const typeDocJson = generatedDocJson.typedocJson; + + // TODO: Extract the non typeDoc exports, and render them somehow + const typeDocNameOrder = _.compact( + _.flatten( + _.map(exportPathOrder, exportPath => { + return exportPathToTypedocNames[exportPath]; + }), + ), + ); + const docAgnosticFormat: DocAgnosticFormat = {}; - _.each(orderedSectionNames, sectionName => { - const modulePathsIfExists = docsInfo.getModulePathsIfExists(sectionName); - if (_.isUndefined(modulePathsIfExists)) { - return; // no-op - } - const packageDefinitions = typeDocUtils.getModuleDefinitionsBySectionName(typeDocJson, modulePathsIfExists); - let packageDefinitionWithMergedChildren; - if (_.isEmpty(packageDefinitions)) { - return; // no-op - } else if (packageDefinitions.length === 1) { - packageDefinitionWithMergedChildren = packageDefinitions[0]; - } else { - // HACK: For now, if there are two modules to display in a single section, - // we simply concat the children. This works for our limited use-case where - // we want to display types stored in two files under a single section - packageDefinitionWithMergedChildren = packageDefinitions[0]; - for (let i = 1; i < packageDefinitions.length; i++) { - packageDefinitionWithMergedChildren.children = [ - ...packageDefinitionWithMergedChildren.children, - ...packageDefinitions[i].children, - ]; + const typeEntities: TypeDocNode[] = []; + _.each(typeDocNameOrder, typeDocName => { + const fileChildIndex = _.findIndex(typeDocJson.children, child => child.name === typeDocName); + const fileChild = typeDocJson.children[fileChildIndex]; + let sectionName: string; + _.each(fileChild.children, child => { + switch (child.kindString) { + case KindString.Class: + case KindString.ObjectLiteral: { + sectionName = child.name; + docsInfo.sections[sectionName] = sectionName; + docsInfo.menu[sectionName] = [sectionName]; + const entities = child.children; + const commentObj = child.comment; + const sectionComment = !_.isUndefined(commentObj) ? commentObj.shortText : ''; + const isClassOrObjectLiteral = true; + const docSection = typeDocUtils._convertEntitiesToDocSection( + entities, + docsInfo, + sectionName, + isClassOrObjectLiteral, + ); + docSection.comment = sectionComment; + docAgnosticFormat[sectionName] = docSection; + break; + } + case KindString.Function: { + sectionName = child.name; + docsInfo.sections[sectionName] = sectionName; + docsInfo.menu[sectionName] = [sectionName]; + const entities = [child]; + const commentObj = child.comment; + const SectionComment = !_.isUndefined(commentObj) ? commentObj.shortText : ''; + const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName); + docSection.comment = SectionComment; + docAgnosticFormat[sectionName] = docSection; + break; + } + case KindString.Interface: + case KindString.Variable: + case KindString.Enumeration: + case KindString.TypeAlias: + typeEntities.push(child); + break; + default: + throw errorUtils.spawnSwitchErr('kindString', child.kindString); } - } - - let entities; - let packageComment = ''; - // HACK: We assume 1 exported class per file - const classChildren = _.filter(packageDefinitionWithMergedChildren.children, (child: TypeDocNode) => { - return child.kindString === KindString.Class; }); - if (classChildren.length > 1 && sectionName !== 'types') { - throw new Error('`react-docs` only supports projects with 1 exported class per file'); - } - const isClassExport = packageDefinitionWithMergedChildren.children[0].kindString === KindString.Class; - const isObjectLiteralExport = - packageDefinitionWithMergedChildren.children[0].kindString === KindString.ObjectLiteral; - if (isClassExport) { - entities = packageDefinitionWithMergedChildren.children[0].children; - const commentObj = packageDefinitionWithMergedChildren.children[0].comment; - packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment; - } else if (isObjectLiteralExport) { - entities = packageDefinitionWithMergedChildren.children[0].children; - const commentObj = packageDefinitionWithMergedChildren.children[0].comment; - packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment; - } else { - entities = packageDefinitionWithMergedChildren.children; - } - - const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName); - docSection.comment = packageComment; - docAgnosticFormat[sectionName] = docSection; }); + if (!_.isEmpty(typeEntities)) { + docsInfo.sections[constants.TYPES_SECTION_NAME] = constants.TYPES_SECTION_NAME; + docsInfo.menu[constants.TYPES_SECTION_NAME] = [constants.TYPES_SECTION_NAME]; + const docSection = typeDocUtils._convertEntitiesToDocSection( + typeEntities, + docsInfo, + constants.TYPES_SECTION_NAME, + ); + docAgnosticFormat[constants.TYPES_SECTION_NAME] = docSection; + } + return docAgnosticFormat; }, - _convertEntitiesToDocSection(entities: TypeDocNode[], docsInfo: DocsInfo, sectionName: string): DocSection { + _convertEntitiesToDocSection( + entities: TypeDocNode[], + docsInfo: DocsInfo, + sectionName: string, + isClassOrObjectLiteral: boolean = false, + ): DocSection { const docSection: DocSection = { comment: '', constructors: [], @@ -140,8 +167,18 @@ export const typeDocUtils = { case KindString.Function: if (entity.flags.isExported) { - const func = typeDocUtils._convertFunction(entity, docsInfo.sections, sectionName, docsInfo.id); - docSection.functions.push(func); + const funcName = (entity as TypeDocNode).signatures[0].name; + const isPublicFunc = !_.startsWith(funcName, '_'); + if (isPublicFunc) { + const func = typeDocUtils._convertFunction( + entity, + docsInfo.sections, + sectionName, + docsInfo.id, + isClassOrObjectLiteral, + ); + docSection.functions.push(func); + } } break; @@ -171,11 +208,18 @@ export const typeDocUtils = { } break; - case KindString.Interface: case KindString.Variable: - case KindString.Enumeration: - case KindString.TypeAlias: - if (docsInfo.isPublicType(entity.name)) { + if (isClassOrObjectLiteral) { + // Render as a property + const property = typeDocUtils._convertProperty( + entity, + docsInfo.sections, + sectionName, + docsInfo.id, + ); + docSection.properties.push(property); + } else { + // Otherwise, render as a type const customType = typeDocUtils._convertCustomType( entity, docsInfo.sections, @@ -190,6 +234,23 @@ export const typeDocUtils = { } break; + case KindString.Interface: + case KindString.Variable: + case KindString.Enumeration: + case KindString.TypeAlias: + const customType = typeDocUtils._convertCustomType( + entity, + docsInfo.sections, + sectionName, + docsInfo.id, + ); + const seenTypeNames = _.map(docSection.types, t => t.name); + const isUnseen = !_.includes(seenTypeNames, customType.name); + if (isUnseen) { + docSection.types.push(customType); + } + break; + case KindString.Class: // We currently do not support more then a single class per file // except for the types section, where we ignore classes since we @@ -211,13 +272,7 @@ export const typeDocUtils = { ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, docId) : undefined; const doesIndexSignatureExist = !_.isUndefined(entity.indexSignature); - const isIndexSignatureArray = _.isArray(entity.indexSignature); - // HACK: TypeDoc Versions <0.9.0 indexSignature is of type TypeDocNode[] - // Versions >0.9.0 have it as type TypeDocNode - const indexSignature = - doesIndexSignatureExist && isIndexSignatureArray - ? (entity.indexSignature as TypeDocNode[])[0] - : (entity.indexSignature as TypeDocNode); + const indexSignature = entity.indexSignature as TypeDocNode; const indexSignatureIfExists = doesIndexSignatureExist ? typeDocUtils._convertIndexSignature(indexSignature, sections, sectionName, docId) : undefined; @@ -276,6 +331,9 @@ export const typeDocUtils = { _convertProperty(entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string): Property { const source = entity.sources[0]; const commentIfExists = !_.isUndefined(entity.comment) ? entity.comment.shortText : undefined; + const isConstructor = false; + const isStatic = _.isUndefined(entity.flags.isStatic) ? false : entity.flags.isStatic; + const callPath = typeDocUtils._getCallPath(sectionName, isStatic, isConstructor, entity.name); const property = { name: entity.name, type: typeDocUtils._convertType(entity.type, sections, sectionName, docId), @@ -284,6 +342,7 @@ export const typeDocUtils = { line: source.line, }, comment: commentIfExists, + callPath, }; return property; }, @@ -299,22 +358,6 @@ export const typeDocUtils = { const hasComment = !_.isUndefined(signature.comment); const isStatic = _.isUndefined(entity.flags.isStatic) ? false : entity.flags.isStatic; - // HACK: we use the fact that the sectionName is the same as the property name at the top-level - // of the public interface. In the future, we shouldn't use this hack but rather get it from the JSON. - let callPath; - if (isConstructor || entity.name === '__type') { - callPath = ''; - // TODO: Get rid of this 0x-specific logic - } else if (docId === 'ZERO_EX_JS') { - const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.'; - callPath = - !_.isUndefined(sections.zeroEx) && sectionName !== sections.zeroEx - ? `${topLevelInterface}${sectionName}.` - : topLevelInterface; - } else { - callPath = `${sectionName}.`; - } - const parameters = _.map(signature.parameters, param => { return typeDocUtils._convertParameter(param, sections, sectionName, docId); }); @@ -323,6 +366,7 @@ export const typeDocUtils = { ? undefined : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, docId); + const callPath = typeDocUtils._getCallPath(sectionName, isStatic, isConstructor, entity.name); const method = { isConstructor, isStatic, @@ -340,11 +384,25 @@ export const typeDocUtils = { }; return method; }, + _getCallPath(sectionName: string, isStatic: boolean, isConstructor: boolean, entityName: string) { + // HACK: we use the fact that the sectionName is the same as the property name at the top-level + // of the public interface. In the future, we shouldn't use this hack but rather get it from the JSON. + let callPath; + if (isConstructor || entityName === '__type') { + callPath = ''; + // TODO: Get rid of this 0x-specific logic + } else { + const prefix = isStatic ? sectionName : `${sectionName[0].toLowerCase()}${sectionName.slice(1)}`; + callPath = `${prefix}.`; + } + return callPath; + }, _convertFunction( entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string, + isObjectLiteral: boolean, ): TypescriptFunction { const signature = entity.signatures[0]; const source = entity.sources[0]; @@ -358,10 +416,17 @@ export const typeDocUtils = { ? undefined : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, docId); + let callPath = ''; + if (isObjectLiteral) { + const isConstructor = false; + const isStatic = false; + callPath = typeDocUtils._getCallPath(sectionName, isStatic, isConstructor, entity.name); + } const func = { name: signature.name, comment: hasComment ? signature.comment.shortText : undefined, returnComment: hasComment && signature.comment.returns ? signature.comment.returns : undefined, + callPath, source: { fileName: source.fileName, line: source.line, @@ -414,10 +479,23 @@ export const typeDocUtils = { return typeDocUtils._convertType(t, sections, sectionName, docId); }); - const isConstructor = false; - const methodIfExists = !_.isUndefined(entity.declaration) - ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, docId) - : undefined; + let indexSignatureIfExists; + let methodIfExists; + const doesIndexSignatureExist = + !_.isUndefined(entity.declaration) && !_.isUndefined(entity.declaration.indexSignature); + if (doesIndexSignatureExist) { + const indexSignature = entity.declaration.indexSignature as TypeDocNode; + indexSignatureIfExists = typeDocUtils._convertIndexSignature(indexSignature, sections, sectionName, docId); + } else if (!_.isUndefined(entity.declaration)) { + const isConstructor = false; + methodIfExists = typeDocUtils._convertMethod( + entity.declaration, + isConstructor, + sections, + sectionName, + docId, + ); + } const elementTypeIfExists = !_.isUndefined(entity.elementType) ? { @@ -434,6 +512,7 @@ export const typeDocUtils = { elementType: elementTypeIfExists, types, method: methodIfExists, + indexSignature: indexSignatureIfExists, }; return type; }, |