aboutsummaryrefslogtreecommitdiffstats
path: root/packages/react-docs
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-03-06 23:31:55 +0800
committerFabio Berger <me@fabioberger.com>2018-03-06 23:31:55 +0800
commit0b1ba9f9971bea9003dfb30fca535c17ce62ad08 (patch)
tree69d1c42682340eb0a1f9378f48be484f1e5b8a58 /packages/react-docs
parentf014a97e9ad583f5bdcece330ce2baf4847a6661 (diff)
downloaddexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.tar
dexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.tar.gz
dexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.tar.bz2
dexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.tar.lz
dexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.tar.xz
dexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.tar.zst
dexon-sol-tools-0b1ba9f9971bea9003dfb30fca535c17ce62ad08.zip
Move Documentation to the `@0xproject/react-docs` package
Diffstat (limited to 'packages/react-docs')
-rw-r--r--packages/react-docs/.npmignore5
-rw-r--r--packages/react-docs/CHANGELOG.md3
-rw-r--r--packages/react-docs/README.md47
-rw-r--r--packages/react-docs/package.json37
-rw-r--r--packages/react-docs/scripts/postpublish.js5
-rw-r--r--packages/react-docs/src/ts/components/badge.tsx56
-rw-r--r--packages/react-docs/src/ts/components/comment.tsx23
-rw-r--r--packages/react-docs/src/ts/components/custom_enum.tsx33
-rw-r--r--packages/react-docs/src/ts/components/docs_info.ts120
-rw-r--r--packages/react-docs/src/ts/components/documentation.tsx337
-rw-r--r--packages/react-docs/src/ts/components/enum.tsx23
-rw-r--r--packages/react-docs/src/ts/components/event_definition.tsx84
-rw-r--r--packages/react-docs/src/ts/components/interface.tsx63
-rw-r--r--packages/react-docs/src/ts/components/method_block.tsx149
-rw-r--r--packages/react-docs/src/ts/components/method_signature.tsx128
-rw-r--r--packages/react-docs/src/ts/components/source_link.tsx23
-rw-r--r--packages/react-docs/src/ts/components/type.tsx231
-rw-r--r--packages/react-docs/src/ts/components/type_definition.tsx128
-rw-r--r--packages/react-docs/src/ts/globals.d.ts7
-rw-r--r--packages/react-docs/src/ts/index.ts20
-rw-r--r--packages/react-docs/src/ts/types.ts266
-rw-r--r--packages/react-docs/src/ts/utils/constants.ts9
-rw-r--r--packages/react-docs/src/ts/utils/doxity_utils.ts175
-rw-r--r--packages/react-docs/src/ts/utils/typedoc_utils.ts370
-rw-r--r--packages/react-docs/src/ts/utils/utils.ts10
-rw-r--r--packages/react-docs/tsconfig.json14
-rw-r--r--packages/react-docs/tslint.json9
27 files changed, 2375 insertions, 0 deletions
diff --git a/packages/react-docs/.npmignore b/packages/react-docs/.npmignore
new file mode 100644
index 000000000..87bc30436
--- /dev/null
+++ b/packages/react-docs/.npmignore
@@ -0,0 +1,5 @@
+.*
+yarn-error.log
+/src/
+/scripts/
+tsconfig.json
diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md
new file mode 100644
index 000000000..7c3ac2adf
--- /dev/null
+++ b/packages/react-docs/CHANGELOG.md
@@ -0,0 +1,3 @@
+# CHANGELOG
+
+## vX.X.X - _TBD, 2018_
diff --git a/packages/react-docs/README.md b/packages/react-docs/README.md
new file mode 100644
index 000000000..9299eb2d4
--- /dev/null
+++ b/packages/react-docs/README.md
@@ -0,0 +1,47 @@
+## @0xproject/react-docs
+
+A full-page React component for rendering beautiful documentation generated with [TypeDoc](http://typedoc.org/) or [Doxity](https://github.com/0xproject/doxity).
+
+## Installation
+
+```bash
+yarn add @0xproject/react-docs
+```
+
+## Contributing
+
+We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+```bash
+yarn build
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json
new file mode 100644
index 000000000..b87d20fec
--- /dev/null
+++ b/packages/react-docs/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@0xproject/react-docs",
+ "version": "0.0.1",
+ "description": "React documentation component for rendering TypeDoc & Doxity generated JSON",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "scripts": {
+ "lint": "tslint --project . 'src/ts/**/*.ts' 'src/ts/**/*.tsx'",
+ "build": "tsc",
+ "build:watch": "tsc -w",
+ "clean": "shx rm -rf lib"
+ },
+ "author": "Fabio Berger",
+ "license": "MIT",
+ "devDependencies": {
+ "@0xproject/tslint-config": "^0.4.9",
+ "@types/lodash": "^4.14.86",
+ "@types/node": "^8.0.53",
+ "@types/material-ui": "0.18.0",
+ "@types/react": "^15.0.15",
+ "@types/react-dom": "^0.14.23",
+ "shx": "^0.2.2",
+ "tslint": "^5.9.1",
+ "typescript": "2.7.1"
+ },
+ "dependencies": {
+ "@0xproject/react-shared": "^0.0.1",
+ "basscss": "^8.0.3",
+ "compare-versions": "^3.0.1",
+ "react-tooltip": "^3.2.7",
+ "material-ui": "^0.17.1",
+ "react": "15.6.1",
+ "react-dom": "15.6.1",
+ "lodash": "^4.17.4",
+ "react-tap-event-plugin": "^2.0.1"
+ }
+}
diff --git a/packages/react-docs/scripts/postpublish.js b/packages/react-docs/scripts/postpublish.js
new file mode 100644
index 000000000..639656c7e
--- /dev/null
+++ b/packages/react-docs/scripts/postpublish.js
@@ -0,0 +1,5 @@
+const postpublish_utils = require('../../../scripts/postpublish_utils');
+const packageJSON = require('../package.json');
+
+const subPackageName = packageJSON.name;
+postpublish_utils.standardPostPublishAsync(subPackageName);
diff --git a/packages/react-docs/src/ts/components/badge.tsx b/packages/react-docs/src/ts/components/badge.tsx
new file mode 100644
index 000000000..b342f2dca
--- /dev/null
+++ b/packages/react-docs/src/ts/components/badge.tsx
@@ -0,0 +1,56 @@
+import { Styles } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+const styles: Styles = {
+ badge: {
+ width: 50,
+ fontSize: 11,
+ height: 10,
+ borderRadius: 5,
+ lineHeight: 0.9,
+ fontFamily: 'Roboto Mono',
+ marginLeft: 3,
+ marginRight: 3,
+ },
+};
+
+export interface BadgeProps {
+ title: string;
+ backgroundColor: string;
+}
+
+export interface BadgeState {
+ isHovering: boolean;
+}
+
+export class Badge extends React.Component<BadgeProps, BadgeState> {
+ constructor(props: BadgeProps) {
+ super(props);
+ this.state = {
+ isHovering: false,
+ };
+ }
+ public render() {
+ const badgeStyle = {
+ ...styles.badge,
+ backgroundColor: this.props.backgroundColor,
+ opacity: this.state.isHovering ? 0.7 : 1,
+ };
+ return (
+ <div
+ className="p1 center"
+ style={badgeStyle}
+ onMouseOver={this._setHoverState.bind(this, true)}
+ onMouseOut={this._setHoverState.bind(this, false)}
+ >
+ {this.props.title}
+ </div>
+ );
+ }
+ private _setHoverState(isHovering: boolean) {
+ this.setState({
+ isHovering,
+ });
+ }
+}
diff --git a/packages/react-docs/src/ts/components/comment.tsx b/packages/react-docs/src/ts/components/comment.tsx
new file mode 100644
index 000000000..0d63d4d31
--- /dev/null
+++ b/packages/react-docs/src/ts/components/comment.tsx
@@ -0,0 +1,23 @@
+import { MarkdownCodeBlock } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as ReactMarkdown from 'react-markdown';
+
+export interface CommentProps {
+ comment: string;
+ className?: string;
+}
+
+const defaultProps = {
+ className: '',
+};
+
+export const Comment: React.SFC<CommentProps> = (props: CommentProps) => {
+ return (
+ <div className={`${props.className} comment`}>
+ <ReactMarkdown source={props.comment} renderers={{ code: MarkdownCodeBlock }} />
+ </div>
+ );
+};
+
+Comment.defaultProps = defaultProps;
diff --git a/packages/react-docs/src/ts/components/custom_enum.tsx b/packages/react-docs/src/ts/components/custom_enum.tsx
new file mode 100644
index 000000000..deb33ff1d
--- /dev/null
+++ b/packages/react-docs/src/ts/components/custom_enum.tsx
@@ -0,0 +1,33 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { CustomType } from '../types';
+import { utils } from '../utils/utils';
+
+const STRING_ENUM_CODE_PREFIX = ' strEnum(';
+
+export interface CustomEnumProps {
+ type: CustomType;
+}
+
+// This component renders custom string enums that was a work-around for versions of
+// TypeScript <2.4.0 that did not support them natively. We keep it around to support
+// older versions of 0x.js <0.9.0
+export function CustomEnum(props: CustomEnumProps) {
+ const type = props.type;
+ if (!_.startsWith(type.defaultValue, STRING_ENUM_CODE_PREFIX)) {
+ utils.consoleLog('We do not yet support `Variable` types that are not strEnums');
+ return null;
+ }
+ // Remove the prefix and postfix, leaving only the strEnum values without quotes.
+ const enumValues = type.defaultValue.slice(10, -3).replace(/'/g, '');
+ return (
+ <span>
+ {`{`}
+ {'\t'}
+ {enumValues}
+ <br />
+ {`}`}
+ </span>
+ );
+}
diff --git a/packages/react-docs/src/ts/components/docs_info.ts b/packages/react-docs/src/ts/components/docs_info.ts
new file mode 100644
index 000000000..509bba89e
--- /dev/null
+++ b/packages/react-docs/src/ts/components/docs_info.ts
@@ -0,0 +1,120 @@
+import { MenuSubsectionsBySection } from '@0xproject/react-shared';
+import compareVersions = require('compare-versions');
+import * as _ from 'lodash';
+
+import {
+ ContractsByVersionByNetworkId,
+ DocAgnosticFormat,
+ DocsInfoConfig,
+ DocsMenu,
+ DoxityDocObj,
+ SectionsMap,
+ SupportedDocJson,
+ TypeDocNode,
+} from '../types';
+import { doxityUtils } from '../utils/doxity_utils';
+import { typeDocUtils } from '../utils/typedoc_utils';
+
+export class DocsInfo {
+ public id: string;
+ public type: SupportedDocJson;
+ public displayName: string;
+ public packageUrl: string;
+ public menu: DocsMenu;
+ public sections: SectionsMap;
+ public sectionNameToMarkdown: { [sectionName: string]: string };
+ public contractsByVersionByNetworkId?: ContractsByVersionByNetworkId;
+ private _docsInfo: DocsInfoConfig;
+ constructor(config: DocsInfoConfig) {
+ this.id = config.id;
+ this.type = config.type;
+ this.displayName = config.displayName;
+ this.packageUrl = config.packageUrl;
+ this.sections = config.sections;
+ this.sectionNameToMarkdown = config.sectionNameToMarkdown;
+ this.contractsByVersionByNetworkId = config.contractsByVersionByNetworkId;
+ this._docsInfo = config;
+ }
+ public isPublicType(typeName: string): boolean {
+ if (_.isUndefined(this._docsInfo.publicTypes)) {
+ return false;
+ }
+ const isPublic = _.includes(this._docsInfo.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 existsInSelectedVersion = compareVersions(selectedVersion, versionIntroducedIfExists) >= 0;
+ return existsInSelectedVersion;
+ } else {
+ return true;
+ }
+ });
+ return finalMenu;
+ }
+ public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection {
+ const menuSubsectionsBySection = {} as MenuSubsectionsBySection;
+ if (_.isUndefined(docAgnosticFormat)) {
+ return menuSubsectionsBySection;
+ }
+
+ const docSections = _.keys(this.sections);
+ _.each(docSections, sectionName => {
+ const docSection = docAgnosticFormat[sectionName];
+ if (_.isUndefined(docSection)) {
+ return; // no-op
+ }
+
+ 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 {
+ 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];
+ }
+ });
+ return menuSubsectionsBySection;
+ }
+ public getTypeDefinitionsByName(docAgnosticFormat: DocAgnosticFormat) {
+ if (_.isUndefined(this.sections.types)) {
+ return {};
+ }
+
+ const typeDocSection = docAgnosticFormat[this.sections.types];
+ const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name');
+ return typeDefinitionByName;
+ }
+ public isVisibleConstructor(sectionName: string): boolean {
+ return _.includes(this._docsInfo.visibleConstructors, sectionName);
+ }
+ public convertToDocAgnosticFormat(docObj: DoxityDocObj | TypeDocNode): DocAgnosticFormat {
+ if (this.type === SupportedDocJson.Doxity) {
+ return doxityUtils.convertToDocAgnosticFormat(docObj as DoxityDocObj);
+ } else {
+ return typeDocUtils.convertToDocAgnosticFormat(docObj as TypeDocNode, this);
+ }
+ }
+}
diff --git a/packages/react-docs/src/ts/components/documentation.tsx b/packages/react-docs/src/ts/components/documentation.tsx
new file mode 100644
index 000000000..62632184c
--- /dev/null
+++ b/packages/react-docs/src/ts/components/documentation.tsx
@@ -0,0 +1,337 @@
+import {
+ colors,
+ constants as sharedConstants,
+ EtherscanLinkSuffixes,
+ MarkdownSection,
+ MenuSubsectionsBySection,
+ NestedSidebarMenu,
+ Networks,
+ SectionHeader,
+ Styles,
+ utils as sharedUtils,
+} from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import CircularProgress from 'material-ui/CircularProgress';
+import * as React from 'react';
+import { scroller } from 'react-scroll';
+
+import {
+ AddressByContractName,
+ DocAgnosticFormat,
+ DoxityDocObj,
+ Event,
+ Property,
+ SolidityMethod,
+ SupportedDocJson,
+ TypeDefinitionByName,
+ TypescriptMethod,
+} from '../types';
+import { utils } from '../utils/utils';
+
+import { Badge } from './badge';
+import { Comment } from './comment';
+import { DocsInfo } from './docs_info';
+import { EventDefinition } from './event_definition';
+import { MethodBlock } from './method_block';
+import { SourceLink } from './source_link';
+import { Type } from './type';
+import { TypeDefinition } from './type_definition';
+
+const TOP_BAR_HEIGHT = 60;
+
+const networkNameToColor: { [network: string]: string } = {
+ [Networks.Kovan]: colors.purple,
+ [Networks.Ropsten]: colors.red,
+ [Networks.Mainnet]: colors.turquois,
+ [Networks.Rinkeby]: colors.darkYellow,
+};
+
+export interface DocumentationProps {
+ location: Location;
+ docsVersion: string;
+ availableDocVersions: string[];
+ docsInfo: DocsInfo;
+ docAgnosticFormat?: DocAgnosticFormat;
+ menuSubsectionsBySection: MenuSubsectionsBySection;
+ sourceUrl: string;
+}
+
+export interface DocumentationState {}
+
+const styles: Styles = {
+ mainContainers: {
+ position: 'absolute',
+ top: 1,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ overflowZ: 'hidden',
+ overflowY: 'scroll',
+ minHeight: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
+ WebkitOverflowScrolling: 'touch',
+ },
+ menuContainer: {
+ borderColor: colors.grey300,
+ maxWidth: 330,
+ marginLeft: 20,
+ },
+};
+
+export class Documentation extends React.Component<DocumentationProps, DocumentationState> {
+ public componentDidUpdate(prevProps: DocumentationProps, prevState: DocumentationState) {
+ if (!_.isEqual(prevProps.docAgnosticFormat, this.props.docAgnosticFormat)) {
+ const hash = this.props.location.hash.slice(1);
+ sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID);
+ }
+ }
+ public render() {
+ return (
+ <div>
+ {_.isUndefined(this.props.docAgnosticFormat) ? (
+ this._renderLoading()
+ ) : (
+ <div style={{ width: '100%', height: '100%', backgroundColor: colors.gray40 }}>
+ <div
+ className="mx-auto max-width-4 flex"
+ style={{ color: colors.grey800, height: `calc(100vh - ${TOP_BAR_HEIGHT}px)` }}
+ >
+ <div
+ className="relative sm-hide xs-hide"
+ style={{ width: '36%', height: `calc(100vh - ${TOP_BAR_HEIGHT}px)` }}
+ >
+ <div
+ className="border-right absolute"
+ style={{
+ ...styles.menuContainer,
+ ...styles.mainContainers,
+ height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
+ }}
+ >
+ <NestedSidebarMenu
+ selectedVersion={this.props.docsVersion}
+ versions={this.props.availableDocVersions}
+ title={this.props.docsInfo.displayName}
+ topLevelMenu={this.props.docsInfo.getMenu(this.props.docsVersion)}
+ menuSubsectionsBySection={this.props.menuSubsectionsBySection}
+ />
+ </div>
+ </div>
+ <div
+ className="relative col lg-col-9 md-col-9 sm-col-12 col-12"
+ style={{ backgroundColor: colors.white }}
+ >
+ <div
+ id={sharedConstants.SCROLL_CONTAINER_ID}
+ style={styles.mainContainers}
+ className="absolute px1"
+ >
+ <div id={sharedConstants.SCROLL_TOP_ID} />
+ {this._renderDocumentation()}
+ </div>
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderLoading() {
+ return (
+ <div className="col col-12" style={styles.mainContainers}>
+ <div
+ className="relative sm-px2 sm-pt2 sm-m1"
+ style={{ height: 122, top: '50%', transform: 'translateY(-50%)' }}
+ >
+ <div className="center pb2">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ <div className="center pt2" style={{ paddingBottom: 11 }}>
+ Loading documentation...
+ </div>
+ </div>
+ </div>
+ );
+ }
+ private _renderDocumentation(): React.ReactNode {
+ const subMenus = _.values(this.props.docsInfo.getMenu());
+ const orderedSectionNames = _.flatten(subMenus);
+
+ const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.props.docAgnosticFormat);
+ const renderedSections = _.map(orderedSectionNames, this._renderSection.bind(this, typeDefinitionByName));
+
+ return renderedSections;
+ }
+ private _renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode {
+ const markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName];
+ if (!_.isUndefined(markdownFileIfExists)) {
+ return (
+ <MarkdownSection
+ key={`markdown-section-${sectionName}`}
+ sectionName={sectionName}
+ markdownContent={markdownFileIfExists}
+ />
+ );
+ }
+
+ const docSection = this.props.docAgnosticFormat[sectionName];
+ if (_.isUndefined(docSection)) {
+ return null;
+ }
+
+ const sortedTypes = _.sortBy(docSection.types, 'name');
+ const typeDefs = _.map(sortedTypes, customType => {
+ return (
+ <TypeDefinition
+ sectionName={sectionName}
+ key={`type-${customType.name}`}
+ customType={customType}
+ docsInfo={this.props.docsInfo}
+ />
+ );
+ });
+
+ const sortedProperties = _.sortBy(docSection.properties, 'name');
+ const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName));
+
+ const sortedMethods = _.sortBy(docSection.methods, 'name');
+ const methodDefs = _.map(sortedMethods, method => {
+ const isConstructor = false;
+ return this._renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName);
+ });
+
+ const sortedEvents = _.sortBy(docSection.events, 'name');
+ const eventDefs = _.map(sortedEvents, (event: Event, i: number) => {
+ return (
+ <EventDefinition
+ key={`event-${event.name}-${i}`}
+ event={event}
+ sectionName={sectionName}
+ docsInfo={this.props.docsInfo}
+ />
+ );
+ });
+ return (
+ <div key={`section-${sectionName}`} className="py2 pr3 md-pl2 sm-pl3">
+ <div className="flex pb2">
+ <div style={{ marginRight: 7 }}>
+ <SectionHeader sectionName={sectionName} />
+ </div>
+ {this._renderNetworkBadgesIfExists(sectionName)}
+ </div>
+ {docSection.comment && <Comment comment={docSection.comment} />}
+ {docSection.constructors.length > 0 &&
+ this.props.docsInfo.isVisibleConstructor(sectionName) && (
+ <div>
+ <h2 className="thin">Constructor</h2>
+ {this._renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)}
+ </div>
+ )}
+ {docSection.properties.length > 0 && (
+ <div>
+ <h2 className="thin">Properties</h2>
+ <div>{propertyDefs}</div>
+ </div>
+ )}
+ {docSection.methods.length > 0 && (
+ <div>
+ <h2 className="thin">Methods</h2>
+ <div>{methodDefs}</div>
+ </div>
+ )}
+ {!_.isUndefined(docSection.events) &&
+ docSection.events.length > 0 && (
+ <div>
+ <h2 className="thin">Events</h2>
+ <div>{eventDefs}</div>
+ </div>
+ )}
+ {!_.isUndefined(typeDefs) &&
+ typeDefs.length > 0 && (
+ <div>
+ <div>{typeDefs}</div>
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderNetworkBadgesIfExists(sectionName: string) {
+ if (this.props.docsInfo.type !== SupportedDocJson.Doxity) {
+ return null;
+ }
+
+ const networkToAddressByContractName = this.props.docsInfo.contractsByVersionByNetworkId[
+ this.props.docsVersion
+ ];
+ const badges = _.map(
+ networkToAddressByContractName,
+ (addressByContractName: AddressByContractName, networkName: string) => {
+ const contractAddress = addressByContractName[sectionName];
+ if (_.isUndefined(contractAddress)) {
+ return null;
+ }
+ const linkIfExists = sharedUtils.getEtherScanLinkIfExists(
+ contractAddress,
+ sharedConstants.NETWORK_ID_BY_NAME[networkName],
+ EtherscanLinkSuffixes.Address,
+ );
+ return (
+ <a
+ key={`badge-${networkName}-${sectionName}`}
+ href={linkIfExists}
+ target="_blank"
+ style={{ color: colors.white, textDecoration: 'none' }}
+ >
+ <Badge title={networkName} backgroundColor={networkNameToColor[networkName]} />
+ </a>
+ );
+ },
+ );
+ return badges;
+ }
+ private _renderConstructors(
+ constructors: SolidityMethod[] | TypescriptMethod[],
+ sectionName: string,
+ typeDefinitionByName: TypeDefinitionByName,
+ ): React.ReactNode {
+ const constructorDefs = _.map(constructors, constructor => {
+ return this._renderMethodBlocks(constructor, sectionName, constructor.isConstructor, typeDefinitionByName);
+ });
+ return <div>{constructorDefs}</div>;
+ }
+ private _renderProperty(sectionName: string, property: Property): React.ReactNode {
+ return (
+ <div key={`property-${property.name}-${property.type.name}`} className="pb3">
+ <code className="hljs">
+ {property.name}:
+ <Type type={property.type} sectionName={sectionName} docsInfo={this.props.docsInfo} />
+ </code>
+ {property.source && (
+ <SourceLink
+ version={this.props.docsVersion}
+ source={property.source}
+ sourceUrl={this.props.sourceUrl}
+ />
+ )}
+ {property.comment && <Comment comment={property.comment} className="py2" />}
+ </div>
+ );
+ }
+ private _renderMethodBlocks(
+ method: SolidityMethod | TypescriptMethod,
+ sectionName: string,
+ isConstructor: boolean,
+ typeDefinitionByName: TypeDefinitionByName,
+ ): React.ReactNode {
+ return (
+ <MethodBlock
+ key={`method-${method.name}-${sectionName}`}
+ sectionName={sectionName}
+ method={method}
+ typeDefinitionByName={typeDefinitionByName}
+ libraryVersion={this.props.docsVersion}
+ docsInfo={this.props.docsInfo}
+ sourceUrl={this.props.sourceUrl}
+ />
+ );
+ }
+}
diff --git a/packages/react-docs/src/ts/components/enum.tsx b/packages/react-docs/src/ts/components/enum.tsx
new file mode 100644
index 000000000..37f82f26e
--- /dev/null
+++ b/packages/react-docs/src/ts/components/enum.tsx
@@ -0,0 +1,23 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { EnumValue } from '../types';
+
+export interface EnumProps {
+ values: EnumValue[];
+}
+
+export function Enum(props: EnumProps) {
+ const values = _.map(props.values, (value, i) => {
+ const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : '';
+ return `\n\t${value.name}${defaultValueIfAny},`;
+ });
+ return (
+ <span>
+ {`{`}
+ {values}
+ <br />
+ {`}`}
+ </span>
+ );
+}
diff --git a/packages/react-docs/src/ts/components/event_definition.tsx b/packages/react-docs/src/ts/components/event_definition.tsx
new file mode 100644
index 000000000..8289650f5
--- /dev/null
+++ b/packages/react-docs/src/ts/components/event_definition.tsx
@@ -0,0 +1,84 @@
+import { AnchorTitle, colors, HeaderSizes } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { Event, EventArg } from '../types';
+
+import { DocsInfo } from './docs_info';
+import { Type } from './type';
+
+export interface EventDefinitionProps {
+ event: Event;
+ sectionName: string;
+ docsInfo: DocsInfo;
+}
+
+export interface EventDefinitionState {
+ shouldShowAnchor: boolean;
+}
+
+export class EventDefinition extends React.Component<EventDefinitionProps, EventDefinitionState> {
+ constructor(props: EventDefinitionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const event = this.props.event;
+ const id = `${this.props.sectionName}-${event.name}`;
+ return (
+ <div
+ id={id}
+ className="pb2"
+ style={{ overflow: 'hidden', width: '100%' }}
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={`Event ${event.name}`}
+ id={id}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ <div style={{ fontSize: 16 }}>
+ <pre>
+ <code className="hljs">{this._renderEventCode()}</code>
+ </pre>
+ </div>
+ </div>
+ );
+ }
+ private _renderEventCode() {
+ const indexed = <span style={{ color: colors.green }}> indexed</span>;
+ const eventArgs = _.map(this.props.event.eventArgs, (eventArg: EventArg) => {
+ const type = (
+ <Type type={eventArg.type} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} />
+ );
+ return (
+ <span key={`eventArg-${eventArg.name}`}>
+ {eventArg.name}
+ {eventArg.isIndexed ? indexed : ''}: {type},
+ </span>
+ );
+ });
+ const argList = _.reduce(eventArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '\n\t', curr];
+ });
+ return (
+ <span>
+ {`{`}
+ <br />
+ {'\t'}
+ {argList}
+ <br />
+ {`}`}
+ </span>
+ );
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
+}
diff --git a/packages/react-docs/src/ts/components/interface.tsx b/packages/react-docs/src/ts/components/interface.tsx
new file mode 100644
index 000000000..1c99495d7
--- /dev/null
+++ b/packages/react-docs/src/ts/components/interface.tsx
@@ -0,0 +1,63 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { CustomType, TypeDocTypes } from '../types';
+
+import { DocsInfo } from './docs_info';
+import { MethodSignature } from './method_signature';
+import { Type } from './type';
+
+export interface InterfaceProps {
+ type: CustomType;
+ sectionName: string;
+ docsInfo: DocsInfo;
+}
+
+export function Interface(props: InterfaceProps) {
+ const type = props.type;
+ const properties = _.map(type.children, property => {
+ return (
+ <span key={`property-${property.name}-${property.type}-${type.name}`}>
+ {property.name}:{' '}
+ {property.type.typeDocType !== TypeDocTypes.Reflection ? (
+ <Type type={property.type} sectionName={props.sectionName} docsInfo={props.docsInfo} />
+ ) : (
+ <MethodSignature
+ method={property.type.method}
+ sectionName={props.sectionName}
+ shouldHideMethodName={true}
+ shouldUseArrowSyntax={true}
+ docsInfo={props.docsInfo}
+ />
+ )},
+ </span>
+ );
+ });
+ const hasIndexSignature = !_.isUndefined(type.indexSignature);
+ if (hasIndexSignature) {
+ 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} />
+ </span>
+ );
+ properties.push(
+ <span key={`indexSignature-${type.name}-${is.keyType.name}`}>
+ [{param}]: {is.valueName},
+ </span>,
+ );
+ }
+ const propertyList = _.reduce(properties, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '\n\t', curr];
+ });
+ return (
+ <span>
+ {`{`}
+ <br />
+ {'\t'}
+ {propertyList}
+ <br />
+ {`}`}
+ </span>
+ );
+}
diff --git a/packages/react-docs/src/ts/components/method_block.tsx b/packages/react-docs/src/ts/components/method_block.tsx
new file mode 100644
index 000000000..5ed7f42a1
--- /dev/null
+++ b/packages/react-docs/src/ts/components/method_block.tsx
@@ -0,0 +1,149 @@
+import { AnchorTitle, colors, HeaderSizes, Styles } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { Parameter, SolidityMethod, TypeDefinitionByName, TypescriptMethod } from '../types';
+import { typeDocUtils } from '../utils/typedoc_utils';
+
+import { Comment } from './comment';
+import { DocsInfo } from './docs_info';
+import { MethodSignature } from './method_signature';
+import { SourceLink } from './source_link';
+
+export interface MethodBlockProps {
+ method: SolidityMethod | TypescriptMethod;
+ sectionName: string;
+ libraryVersion: string;
+ typeDefinitionByName: TypeDefinitionByName;
+ docsInfo: DocsInfo;
+ sourceUrl: string;
+}
+
+export interface MethodBlockState {
+ shouldShowAnchor: boolean;
+}
+
+const styles: Styles = {
+ chip: {
+ fontSize: 13,
+ backgroundColor: colors.lightBlueA700,
+ color: colors.white,
+ height: 11,
+ borderRadius: 14,
+ lineHeight: 0.9,
+ },
+};
+
+export class MethodBlock extends React.Component<MethodBlockProps, MethodBlockState> {
+ constructor(props: MethodBlockProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const method = this.props.method;
+ if (typeDocUtils.isPrivateOrProtectedProperty(method.name)) {
+ return null;
+ }
+
+ return (
+ <div
+ id={`${this.props.sectionName}-${method.name}`}
+ style={{ overflow: 'hidden', width: '100%' }}
+ className="pb4"
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ {!method.isConstructor && (
+ <div className="flex pb2 pt2">
+ {(method as TypescriptMethod).isStatic && this._renderChip('Static')}
+ {(method as SolidityMethod).isConstant && this._renderChip('Constant')}
+ {(method as SolidityMethod).isPayable && this._renderChip('Payable')}
+ <div style={{ lineHeight: 1.3 }}>
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={method.name}
+ id={`${this.props.sectionName}-${method.name}`}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ </div>
+ </div>
+ )}
+ <code className="hljs">
+ <MethodSignature
+ method={method}
+ sectionName={this.props.sectionName}
+ typeDefinitionByName={this.props.typeDefinitionByName}
+ docsInfo={this.props.docsInfo}
+ />
+ </code>
+ {(method as TypescriptMethod).source && (
+ <SourceLink
+ version={this.props.libraryVersion}
+ source={(method as TypescriptMethod).source}
+ sourceUrl={this.props.sourceUrl}
+ />
+ )}
+ {method.comment && <Comment comment={method.comment} className="py2" />}
+ {method.parameters &&
+ !_.isEmpty(method.parameters) && (
+ <div>
+ <h4 className="pb1 thin" style={{ borderBottom: '1px solid #e1e8ed' }}>
+ ARGUMENTS
+ </h4>
+ {this._renderParameterDescriptions(method.parameters)}
+ </div>
+ )}
+ {method.returnComment && (
+ <div className="pt1 comment">
+ <h4 className="pb1 thin" style={{ borderBottom: '1px solid #e1e8ed' }}>
+ RETURNS
+ </h4>
+ <Comment comment={method.returnComment} />
+ </div>
+ )}
+ </div>
+ );
+ }
+ private _renderChip(text: string) {
+ return (
+ <div className="p1 mr1" style={styles.chip}>
+ {text}
+ </div>
+ );
+ }
+ private _renderParameterDescriptions(parameters: Parameter[]) {
+ const descriptions = _.map(parameters, parameter => {
+ const isOptional = parameter.isOptional;
+ return (
+ <div
+ key={`param-description-${parameter.name}`}
+ className="flex pb1 mb2"
+ style={{ borderBottom: '1px solid #f0f4f7' }}
+ >
+ <div className="pl2 col lg-col-4 md-col-4 sm-col-12 col-12">
+ <div
+ className="bold"
+ style={{ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}
+ >
+ {parameter.name}
+ </div>
+ <div className="pt1" style={{ color: colors.grey, fontSize: 14 }}>
+ {isOptional && 'optional'}
+ </div>
+ </div>
+ <div className="col lg-col-8 md-col-8 sm-col-12 col-12" style={{ paddingLeft: 5 }}>
+ {parameter.comment && <Comment comment={parameter.comment} />}
+ </div>
+ </div>
+ );
+ });
+ return descriptions;
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
+}
diff --git a/packages/react-docs/src/ts/components/method_signature.tsx b/packages/react-docs/src/ts/components/method_signature.tsx
new file mode 100644
index 000000000..e21d82287
--- /dev/null
+++ b/packages/react-docs/src/ts/components/method_signature.tsx
@@ -0,0 +1,128 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as ReactDOM from 'react-dom';
+
+import { Parameter, SolidityMethod, TypeDefinitionByName, TypescriptMethod } from '../types';
+import { constants } from '../utils/constants';
+
+import { DocsInfo } from './docs_info';
+import { Type } from './type';
+
+export interface MethodSignatureProps {
+ method: TypescriptMethod | SolidityMethod;
+ sectionName: string;
+ shouldHideMethodName?: boolean;
+ shouldUseArrowSyntax?: boolean;
+ typeDefinitionByName?: TypeDefinitionByName;
+ docsInfo: DocsInfo;
+}
+
+const defaultProps = {
+ shouldHideMethodName: false,
+ shouldUseArrowSyntax: false,
+};
+
+export const MethodSignature: React.SFC<MethodSignatureProps> = (props: MethodSignatureProps) => {
+ const sectionName = constants.TYPES_SECTION_NAME;
+ const parameters = renderParameters(props.method, 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.
+ // Ideally we would check the character length of the resulting method signature and
+ // if it exceeds the available space, put params on their own lines.
+ const hasMoreThenTwoParams = parameters.length > 2;
+ _.each(parameters, (param: React.ReactNode, i: number) => {
+ const finalParam = hasMoreThenTwoParams ? (
+ <span className="pl2" key={`param-${i}`}>
+ {param}
+ </span>
+ ) : (
+ param
+ );
+ paramStringArray.push(finalParam);
+ const comma = hasMoreThenTwoParams ? (
+ <span key={`param-comma-${i}`}>
+ , <br />
+ </span>
+ ) : (
+ ', '
+ );
+ paramStringArray.push(comma);
+ });
+ if (!hasMoreThenTwoParams) {
+ paramStringArray.pop();
+ }
+ const methodName = props.shouldHideMethodName ? '' : props.method.name;
+ const typeParameterIfExists = _.isUndefined((props.method as TypescriptMethod).typeParameter)
+ ? undefined
+ : renderTypeParameter(props.method, props.docsInfo, sectionName, props.typeDefinitionByName);
+ return (
+ <span style={{ fontSize: 15 }}>
+ {props.method.callPath}
+ {methodName}
+ {typeParameterIfExists}({hasMoreThenTwoParams && <br />}
+ {paramStringArray})
+ {props.method.returnType && (
+ <span>
+ {props.shouldUseArrowSyntax ? ' => ' : ': '}{' '}
+ <Type
+ type={props.method.returnType}
+ sectionName={sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ </span>
+ )}
+ </span>
+ );
+};
+
+MethodSignature.defaultProps = defaultProps;
+
+function renderParameters(
+ method: TypescriptMethod | SolidityMethod,
+ docsInfo: DocsInfo,
+ sectionName: string,
+ typeDefinitionByName?: TypeDefinitionByName,
+) {
+ const parameters = method.parameters;
+ const params = _.map(parameters, (p: Parameter) => {
+ const isOptional = p.isOptional;
+ const type = (
+ <Type
+ type={p.type}
+ sectionName={sectionName}
+ typeDefinitionByName={typeDefinitionByName}
+ docsInfo={docsInfo}
+ />
+ );
+ return (
+ <span key={`param-${p.type}-${p.name}`}>
+ {p.name}
+ {isOptional && '?'}: {type}
+ </span>
+ );
+ });
+ return params;
+}
+
+function renderTypeParameter(
+ method: TypescriptMethod,
+ docsInfo: DocsInfo,
+ sectionName: string,
+ typeDefinitionByName?: TypeDefinitionByName,
+) {
+ const typeParameter = method.typeParameter;
+ const typeParam = (
+ <span>
+ {`<${typeParameter.name} extends `}
+ <Type
+ type={typeParameter.type}
+ sectionName={sectionName}
+ typeDefinitionByName={typeDefinitionByName}
+ docsInfo={docsInfo}
+ />
+ {`>`}
+ </span>
+ );
+ return typeParam;
+}
diff --git a/packages/react-docs/src/ts/components/source_link.tsx b/packages/react-docs/src/ts/components/source_link.tsx
new file mode 100644
index 000000000..89956a507
--- /dev/null
+++ b/packages/react-docs/src/ts/components/source_link.tsx
@@ -0,0 +1,23 @@
+import { colors } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { Source } from '../types';
+
+export interface SourceLinkProps {
+ source: Source;
+ sourceUrl: string;
+ version: string;
+}
+
+export function SourceLink(props: SourceLinkProps) {
+ const src = props.source;
+ const sourceCodeUrl = `${props.sourceUrl}/${src.fileName}#L${src.line}`;
+ return (
+ <div className="pt2" style={{ fontSize: 14 }}>
+ <a href={sourceCodeUrl} target="_blank" className="underline" style={{ color: colors.grey }}>
+ Source
+ </a>
+ </div>
+ );
+}
diff --git a/packages/react-docs/src/ts/components/type.tsx b/packages/react-docs/src/ts/components/type.tsx
new file mode 100644
index 000000000..780d87eae
--- /dev/null
+++ b/packages/react-docs/src/ts/components/type.tsx
@@ -0,0 +1,231 @@
+import { colors, constants as sharedConstants, utils as sharedUtils } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+import { Link as ScrollLink } from 'react-scroll';
+import * as ReactTooltip from 'react-tooltip';
+
+import { Type as TypeDef, TypeDefinitionByName, TypeDocTypes } from '../types';
+import { constants } from '../utils/constants';
+import { utils } from '../utils/utils';
+
+import { DocsInfo } from './docs_info';
+import { TypeDefinition } from './type_definition';
+
+// Some types reference other libraries. For these types, we want to link the user to the relevant documentation.
+const typeToUrl: { [typeName: string]: string } = {
+ Web3: constants.URL_WEB3_DOCS,
+ Provider: constants.URL_WEB3_PROVIDER_DOCS,
+ BigNumber: constants.URL_BIGNUMBERJS_GITHUB,
+ DecodedLogEntryEvent: constants.URL_WEB3_DECODED_LOG_ENTRY_EVENT,
+ LogEntryEvent: constants.URL_WEB3_LOG_ENTRY_EVENT,
+};
+
+const typePrefix: { [typeName: string]: string } = {
+ Provider: 'Web3',
+ DecodedLogEntryEvent: 'Web3',
+ LogEntryEvent: 'Web3',
+};
+
+const typeToSection: { [typeName: string]: string } = {
+ ExchangeWrapper: 'exchange',
+ TokenWrapper: 'token',
+ TokenRegistryWrapper: 'tokenRegistry',
+ EtherTokenWrapper: 'etherToken',
+ ProxyWrapper: 'proxy',
+ TokenTransferProxyWrapper: 'proxy',
+ OrderStateWatcher: 'orderWatcher',
+};
+
+export interface TypeProps {
+ type: TypeDef;
+ docsInfo: DocsInfo;
+ sectionName: string;
+ typeDefinitionByName?: TypeDefinitionByName;
+}
+
+// The return type needs to be `any` here so that we can recursively define <Type /> components within
+// <Type /> components (e.g when rendering the union type).
+export function Type(props: TypeProps): any {
+ const type = props.type;
+ const isReference = type.typeDocType === TypeDocTypes.Reference;
+ const isArray = type.typeDocType === TypeDocTypes.Array;
+ let typeNameColor = 'inherit';
+ let typeName: string | React.ReactNode;
+ let typeArgs: React.ReactNode[] = [];
+ switch (type.typeDocType) {
+ case TypeDocTypes.Intrinsic:
+ case TypeDocTypes.Unknown:
+ typeName = type.name;
+ typeNameColor = colors.orange;
+ break;
+
+ case TypeDocTypes.Reference:
+ typeName = type.name;
+ typeArgs = _.map(type.typeArguments, (arg: TypeDef) => {
+ if (arg.typeDocType === TypeDocTypes.Array) {
+ const key = `type-${arg.elementType.name}-${arg.elementType.typeDocType}`;
+ return (
+ <span>
+ <Type
+ key={key}
+ type={arg.elementType}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />[]
+ </span>
+ );
+ } else {
+ const subType = (
+ <Type
+ key={`type-${arg.name}-${arg.value}-${arg.typeDocType}`}
+ type={arg}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ );
+ return subType;
+ }
+ });
+ break;
+
+ case TypeDocTypes.StringLiteral:
+ typeName = `'${type.value}'`;
+ typeNameColor = colors.green;
+ break;
+
+ case TypeDocTypes.Array:
+ typeName = type.elementType.name;
+ break;
+
+ case TypeDocTypes.Union:
+ const unionTypes = _.map(type.types, t => {
+ return (
+ <Type
+ key={`type-${t.name}-${t.value}-${t.typeDocType}`}
+ type={t}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ );
+ });
+ typeName = _.reduce(unionTypes, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '|', curr];
+ });
+ break;
+
+ case TypeDocTypes.TypeParameter:
+ typeName = type.name;
+ break;
+
+ case TypeDocTypes.Intersection:
+ const intersectionsTypes = _.map(type.types, t => {
+ return (
+ <Type
+ key={`type-${t.name}-${t.value}-${t.typeDocType}`}
+ type={t}
+ sectionName={props.sectionName}
+ typeDefinitionByName={props.typeDefinitionByName}
+ docsInfo={props.docsInfo}
+ />
+ );
+ });
+ typeName = _.reduce(intersectionsTypes, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '&', curr];
+ });
+ break;
+
+ default:
+ throw utils.spawnSwitchErr('type.typeDocType', type.typeDocType);
+ }
+ // HACK: Normalize BigNumber to simply BigNumber. For some reason the type
+ // name is unpredictably one or the other.
+ if (typeName === 'BigNumber') {
+ typeName = 'BigNumber';
+ }
+ const commaSeparatedTypeArgs = _.reduce(typeArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, ', ', curr];
+ });
+
+ const typeNameUrlIfExists = typeToUrl[typeName as string];
+ const typePrefixIfExists = typePrefix[typeName as string];
+ const sectionNameIfExists = typeToSection[typeName as string];
+ if (!_.isUndefined(typeNameUrlIfExists)) {
+ typeName = (
+ <a
+ href={typeNameUrlIfExists}
+ target="_blank"
+ className="text-decoration-none"
+ style={{ color: colors.lightBlueA700 }}
+ >
+ {!_.isUndefined(typePrefixIfExists) ? `${typePrefixIfExists}.` : ''}
+ {typeName}
+ </a>
+ );
+ } else if (
+ (isReference || isArray) &&
+ (props.docsInfo.isPublicType(typeName as string) || !_.isUndefined(sectionNameIfExists))
+ ) {
+ const id = Math.random().toString();
+ const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists)
+ ? `${props.sectionName}-${typeName}`
+ : sectionNameIfExists;
+ let typeDefinition;
+ if (props.typeDefinitionByName) {
+ typeDefinition = props.typeDefinitionByName[typeName as string];
+ }
+ typeName = (
+ <ScrollLink
+ to={typeDefinitionAnchorId}
+ offset={0}
+ 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>
+ ) : (
+ <span
+ data-tip={true}
+ data-for={id}
+ onClick={sharedUtils.setUrlHash.bind(null, typeDefinitionAnchorId)}
+ style={{
+ color: colors.lightBlueA700,
+ cursor: 'pointer',
+ display: 'inline-block',
+ }}
+ >
+ {typeName}
+ <ReactTooltip type="light" effect="solid" id={id} className="typeTooltip">
+ <TypeDefinition
+ sectionName={props.sectionName}
+ customType={typeDefinition}
+ shouldAddId={false}
+ docsInfo={props.docsInfo}
+ />
+ </ReactTooltip>
+ </span>
+ )}
+ </ScrollLink>
+ );
+ }
+ return (
+ <span>
+ <span style={{ color: typeNameColor }}>{typeName}</span>
+ {isArray && '[]'}
+ {!_.isEmpty(typeArgs) && (
+ <span>
+ {'<'}
+ {commaSeparatedTypeArgs}
+ {'>'}
+ </span>
+ )}
+ </span>
+ );
+}
diff --git a/packages/react-docs/src/ts/components/type_definition.tsx b/packages/react-docs/src/ts/components/type_definition.tsx
new file mode 100644
index 000000000..944a31f95
--- /dev/null
+++ b/packages/react-docs/src/ts/components/type_definition.tsx
@@ -0,0 +1,128 @@
+import { AnchorTitle, colors, HeaderSizes } from '@0xproject/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { CustomType, CustomTypeChild, KindString, TypeDocTypes } from '../types';
+import { utils } from '../utils/utils';
+
+import { Comment } from './comment';
+import { CustomEnum } from './custom_enum';
+import { DocsInfo } from './docs_info';
+import { Enum } from './enum';
+import { Interface } from './interface';
+import { MethodSignature } from './method_signature';
+import { Type } from './type';
+
+export interface TypeDefinitionProps {
+ sectionName: string;
+ customType: CustomType;
+ shouldAddId?: boolean;
+ docsInfo: DocsInfo;
+}
+
+export interface TypeDefinitionState {
+ shouldShowAnchor: boolean;
+}
+
+export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDefinitionState> {
+ public static defaultProps: Partial<TypeDefinitionProps> = {
+ shouldAddId: true,
+ };
+ constructor(props: TypeDefinitionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const customType = this.props.customType;
+ if (!this.props.docsInfo.isPublicType(customType.name)) {
+ return null; // no-op
+ }
+
+ let typePrefix: string;
+ let codeSnippet: React.ReactNode;
+ switch (customType.kindString) {
+ case KindString.Interface:
+ typePrefix = 'Interface';
+ codeSnippet = (
+ <Interface type={customType} sectionName={this.props.sectionName} docsInfo={this.props.docsInfo} />
+ );
+ break;
+
+ case KindString.Variable:
+ typePrefix = 'Enum';
+ codeSnippet = <CustomEnum type={customType} />;
+ break;
+
+ case KindString.Enumeration:
+ typePrefix = 'Enum';
+ const enumValues = _.map(customType.children, (c: CustomTypeChild) => {
+ return {
+ name: c.name,
+ defaultValue: c.defaultValue,
+ };
+ });
+ codeSnippet = <Enum values={enumValues} />;
+ break;
+
+ case KindString.TypeAlias:
+ typePrefix = 'Type Alias';
+ codeSnippet = (
+ <span>
+ <span style={{ color: colors.lightPurple }}>type</span> {customType.name} ={' '}
+ {customType.type.typeDocType !== TypeDocTypes.Reflection ? (
+ <Type
+ type={customType.type}
+ sectionName={this.props.sectionName}
+ docsInfo={this.props.docsInfo}
+ />
+ ) : (
+ <MethodSignature
+ method={customType.type.method}
+ sectionName={this.props.sectionName}
+ shouldHideMethodName={true}
+ shouldUseArrowSyntax={true}
+ docsInfo={this.props.docsInfo}
+ />
+ )}
+ </span>
+ );
+ break;
+
+ default:
+ throw utils.spawnSwitchErr('type.kindString', customType.kindString);
+ }
+
+ const typeDefinitionAnchorId = `${this.props.sectionName}-${customType.name}`;
+ return (
+ <div
+ id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
+ className="pb2"
+ style={{ overflow: 'hidden', width: '100%' }}
+ onMouseOver={this._setAnchorVisibility.bind(this, true)}
+ onMouseOut={this._setAnchorVisibility.bind(this, false)}
+ >
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={`${typePrefix} ${customType.name}`}
+ id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ <div style={{ fontSize: 16 }}>
+ <pre>
+ <code className="hljs">{codeSnippet}</code>
+ </pre>
+ </div>
+ <div style={{ maxWidth: 620 }}>
+ {customType.comment && <Comment comment={customType.comment} className="py2" />}
+ </div>
+ </div>
+ );
+ }
+ private _setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
+}
diff --git a/packages/react-docs/src/ts/globals.d.ts b/packages/react-docs/src/ts/globals.d.ts
new file mode 100644
index 000000000..31c8a2c1f
--- /dev/null
+++ b/packages/react-docs/src/ts/globals.d.ts
@@ -0,0 +1,7 @@
+declare module 'react-tooltip';
+
+// compare-version declarations
+declare function compareVersions(firstVersion: string, secondVersion: string): number;
+declare module 'compare-versions' {
+ export = compareVersions;
+}
diff --git a/packages/react-docs/src/ts/index.ts b/packages/react-docs/src/ts/index.ts
new file mode 100644
index 000000000..ee2950c0e
--- /dev/null
+++ b/packages/react-docs/src/ts/index.ts
@@ -0,0 +1,20 @@
+export { Documentation } from './components/documentation';
+export { DocsInfo } from './components/docs_info';
+
+// Exported to give users of this library added flexibility if they want to build
+// a docs page from scratch using the individual components.
+export { Badge } from './components/badge';
+export { Comment } from './components/comment';
+export { CustomEnum } from './components/custom_enum';
+export { Enum } from './components/enum';
+export { EventDefinition } from './components/event_definition';
+export { Interface } from './components/interface';
+export { MethodBlock } from './components/method_block';
+export { MethodSignature } from './components/method_signature';
+export { SourceLink } from './components/source_link';
+export { TypeDefinition } from './components/type_definition';
+export { Type } from './components/type';
+
+export { DocsInfoConfig, DocAgnosticFormat, DoxityDocObj, DocsMenu, SupportedDocJson, TypeDocNode } from './types';
+
+export { constants } from './utils/constants';
diff --git a/packages/react-docs/src/ts/types.ts b/packages/react-docs/src/ts/types.ts
new file mode 100644
index 000000000..cb211e887
--- /dev/null
+++ b/packages/react-docs/src/ts/types.ts
@@ -0,0 +1,266 @@
+export interface DocsInfoConfig {
+ id: string;
+ type: SupportedDocJson;
+ displayName: string;
+ packageUrl: string;
+ menu: DocsMenu;
+ sections: SectionsMap;
+ sectionNameToMarkdown: { [sectionName: string]: string };
+ visibleConstructors: string[];
+ subPackageName?: string;
+ publicTypes?: string[];
+ sectionNameToModulePath?: { [sectionName: string]: string[] };
+ menuSubsectionToVersionWhenIntroduced?: { [sectionName: string]: string };
+ contractsByVersionByNetworkId?: ContractsByVersionByNetworkId;
+}
+
+export interface DocsMenu {
+ [sectionName: string]: string[];
+}
+
+export interface SectionsMap {
+ [sectionName: string]: string;
+}
+
+export interface TypeDocType {
+ type: TypeDocTypes;
+ value: string;
+ name: string;
+ types: TypeDocType[];
+ typeArguments?: TypeDocType[];
+ declaration: TypeDocNode;
+ elementType?: TypeDocType;
+}
+
+export interface TypeDocFlags {
+ isStatic?: boolean;
+ isOptional?: boolean;
+ isPublic?: boolean;
+}
+
+export interface TypeDocGroup {
+ title: string;
+ children: number[];
+}
+
+export interface TypeDocNode {
+ id?: number;
+ name?: string;
+ kind?: string;
+ defaultValue?: string;
+ kindString?: string;
+ type?: TypeDocType;
+ fileName?: string;
+ line?: number;
+ comment?: TypeDocNode;
+ text?: string;
+ shortText?: string;
+ returns?: string;
+ declaration: TypeDocNode;
+ flags?: TypeDocFlags;
+ indexSignature?: TypeDocNode | TypeDocNode[]; // TypeDocNode in TypeDoc <V0.9.0, TypeDocNode[] in >V0.9.0
+ signatures?: TypeDocNode[];
+ parameters?: TypeDocNode[];
+ typeParameter?: TypeDocNode[];
+ sources?: TypeDocNode[];
+ children?: TypeDocNode[];
+ groups?: TypeDocGroup[];
+}
+
+export enum TypeDocTypes {
+ Intrinsic = 'intrinsic',
+ Reference = 'reference',
+ Array = 'array',
+ StringLiteral = 'stringLiteral',
+ Reflection = 'reflection',
+ Union = 'union',
+ TypeParameter = 'typeParameter',
+ Intersection = 'intersection',
+ Unknown = 'unknown',
+}
+
+// Exception: We don't make the values uppercase because these KindString's need to
+// match up those returned by TypeDoc
+export enum KindString {
+ Constructor = 'Constructor',
+ Property = 'Property',
+ Method = 'Method',
+ Interface = 'Interface',
+ TypeAlias = 'Type alias',
+ Variable = 'Variable',
+ Function = 'Function',
+ Enumeration = 'Enumeration',
+}
+
+export interface DocAgnosticFormat {
+ [sectionName: string]: DocSection;
+}
+
+export interface DocSection {
+ comment: string;
+ constructors: Array<TypescriptMethod | SolidityMethod>;
+ methods: Array<TypescriptMethod | SolidityMethod>;
+ properties: Property[];
+ types: CustomType[];
+ events?: Event[];
+}
+
+export interface TypescriptMethod extends BaseMethod {
+ source?: Source;
+ isStatic?: boolean;
+ typeParameter?: TypeParameter;
+}
+
+export interface SolidityMethod extends BaseMethod {
+ isConstant?: boolean;
+ isPayable?: boolean;
+}
+
+export interface Source {
+ fileName: string;
+ line: number;
+}
+
+export interface Parameter {
+ name: string;
+ comment: string;
+ isOptional: boolean;
+ type: Type;
+}
+
+export interface TypeParameter {
+ name: string;
+ type: Type;
+}
+
+export interface Type {
+ name: string;
+ typeDocType: TypeDocTypes;
+ value?: string;
+ typeArguments?: Type[];
+ elementType?: ElementType;
+ types?: Type[];
+ method?: TypescriptMethod;
+}
+
+export interface ElementType {
+ name: string;
+ typeDocType: TypeDocTypes;
+}
+
+export interface IndexSignature {
+ keyName: string;
+ keyType: Type;
+ valueName: string;
+}
+
+export interface CustomType {
+ name: string;
+ kindString: string;
+ type?: Type;
+ method?: TypescriptMethod;
+ indexSignature?: IndexSignature;
+ defaultValue?: string;
+ comment?: string;
+ children?: CustomTypeChild[];
+}
+
+export interface CustomTypeChild {
+ name: string;
+ type?: Type;
+ defaultValue?: string;
+}
+
+export interface Event {
+ name: string;
+ eventArgs: EventArg[];
+}
+
+export interface EventArg {
+ isIndexed: boolean;
+ name: string;
+ type: Type;
+}
+
+export interface Property {
+ name: string;
+ type: Type;
+ source?: Source;
+ comment?: string;
+}
+
+export interface BaseMethod {
+ isConstructor: boolean;
+ name: string;
+ returnComment?: string | undefined;
+ callPath: string;
+ parameters: Parameter[];
+ returnType: Type;
+ comment?: string;
+}
+
+export interface TypeDefinitionByName {
+ [typeName: string]: CustomType;
+}
+
+export enum SupportedDocJson {
+ Doxity = 'DOXITY',
+ TypeDoc = 'TYPEDOC',
+}
+
+export interface ContractsByVersionByNetworkId {
+ [version: string]: {
+ [networkName: string]: {
+ [contractName: string]: string;
+ };
+ };
+}
+
+export interface DoxityDocObj {
+ [contractName: string]: DoxityContractObj;
+}
+
+export interface DoxityContractObj {
+ title: string;
+ fileName: string;
+ name: string;
+ abiDocs: DoxityAbiDoc[];
+}
+
+export interface DoxityAbiDoc {
+ constant: boolean;
+ inputs: DoxityInput[];
+ name: string;
+ outputs: DoxityOutput[];
+ payable: boolean;
+ type: string;
+ details?: string;
+ return?: string;
+}
+
+export interface DoxityOutput {
+ name: string;
+ type: string;
+}
+
+export interface DoxityInput {
+ name: string;
+ type: string;
+ description: string;
+ indexed?: boolean;
+}
+
+export interface AddressByContractName {
+ [contractName: string]: string;
+}
+
+export interface EnumValue {
+ name: string;
+ defaultValue?: string;
+}
+
+export enum AbiTypes {
+ Constructor = 'constructor',
+ Function = 'function',
+ Event = 'event',
+}
diff --git a/packages/react-docs/src/ts/utils/constants.ts b/packages/react-docs/src/ts/utils/constants.ts
new file mode 100644
index 000000000..6692ce7e4
--- /dev/null
+++ b/packages/react-docs/src/ts/utils/constants.ts
@@ -0,0 +1,9 @@
+export const constants = {
+ TYPES_SECTION_NAME: 'types',
+ URL_WEB3_DOCS: 'https://github.com/ethereum/wiki/wiki/JavaScript-API',
+ URL_WEB3_DECODED_LOG_ENTRY_EVENT:
+ 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L123',
+ URL_WEB3_LOG_ENTRY_EVENT: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L127',
+ URL_WEB3_PROVIDER_DOCS: 'https://github.com/0xProject/web3-typescript-typings/blob/f5bcb96/index.d.ts#L150',
+ URL_BIGNUMBERJS_GITHUB: 'http://mikemcl.github.io/bignumber.js',
+};
diff --git a/packages/react-docs/src/ts/utils/doxity_utils.ts b/packages/react-docs/src/ts/utils/doxity_utils.ts
new file mode 100644
index 000000000..26dea6966
--- /dev/null
+++ b/packages/react-docs/src/ts/utils/doxity_utils.ts
@@ -0,0 +1,175 @@
+import * as _ from 'lodash';
+
+import {
+ AbiTypes,
+ DocAgnosticFormat,
+ DocSection,
+ DoxityAbiDoc,
+ DoxityContractObj,
+ DoxityDocObj,
+ DoxityInput,
+ EventArg,
+ Parameter,
+ Property,
+ SolidityMethod,
+ Type,
+ TypeDocTypes,
+} from '../types';
+
+export const doxityUtils = {
+ convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat {
+ const docAgnosticFormat: DocAgnosticFormat = {};
+ _.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => {
+ const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => {
+ return abiDoc.type === AbiTypes.Constructor;
+ });
+ const constructors = [];
+ if (!_.isUndefined(doxityConstructor)) {
+ const constructor = {
+ isConstructor: true,
+ name: doxityContractObj.name,
+ comment: doxityConstructor.details,
+ returnComment: doxityConstructor.return,
+ callPath: '',
+ parameters: this._convertParameters(doxityConstructor.inputs),
+ returnType: this._convertType(doxityContractObj.name),
+ };
+ constructors.push(constructor);
+ }
+
+ const doxityMethods: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
+ doxityContractObj.abiDocs,
+ (abiDoc: DoxityAbiDoc) => {
+ return this._isMethod(abiDoc);
+ },
+ );
+ const methods: SolidityMethod[] = _.map<DoxityAbiDoc, SolidityMethod>(
+ doxityMethods,
+ (doxityMethod: DoxityAbiDoc) => {
+ const outputs = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs : [];
+ let returnTypeIfExists: Type;
+ if (outputs.length === 0) {
+ // no-op. It's already undefined
+ } else if (outputs.length === 1) {
+ const outputsType = outputs[0].type;
+ returnTypeIfExists = this._convertType(outputsType);
+ } else {
+ const outputsType = `[${_.map(outputs, output => output.type).join(', ')}]`;
+ returnTypeIfExists = this._convertType(outputsType);
+ }
+ // For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken
+ const callPath =
+ contractName !== 'ZRXToken'
+ ? `${contractName[0].toLowerCase()}${contractName.slice(1)}.`
+ : `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`;
+ const method = {
+ isConstructor: false,
+ isConstant: doxityMethod.constant,
+ isPayable: doxityMethod.payable,
+ name: doxityMethod.name,
+ comment: doxityMethod.details,
+ returnComment: doxityMethod.return,
+ callPath,
+ parameters: this._convertParameters(doxityMethod.inputs),
+ returnType: returnTypeIfExists,
+ };
+ return method;
+ },
+ );
+
+ const doxityProperties: DoxityAbiDoc[] = _.filter<DoxityAbiDoc>(
+ doxityContractObj.abiDocs,
+ (abiDoc: DoxityAbiDoc) => {
+ return this._isProperty(abiDoc);
+ },
+ );
+ const properties = _.map<DoxityAbiDoc, Property>(doxityProperties, (doxityProperty: DoxityAbiDoc) => {
+ // We assume that none of our functions return more then a single return value
+ let typeName = doxityProperty.outputs[0].type;
+ if (!_.isEmpty(doxityProperty.inputs)) {
+ // Properties never have more then a single input
+ typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`;
+ }
+ const property = {
+ name: doxityProperty.name,
+ type: this._convertType(typeName),
+ comment: doxityProperty.details,
+ };
+ return property;
+ });
+
+ const doxityEvents = _.filter(
+ doxityContractObj.abiDocs,
+ (abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event,
+ );
+ const events = _.map(doxityEvents, doxityEvent => {
+ const event = {
+ name: doxityEvent.name,
+ eventArgs: this._convertEventArgs(doxityEvent.inputs),
+ };
+ return event;
+ });
+
+ const docSection: DocSection = {
+ comment: doxityContractObj.title,
+ constructors,
+ methods,
+ properties,
+ types: [],
+ events,
+ };
+ docAgnosticFormat[contractName] = docSection;
+ });
+ return docAgnosticFormat;
+ },
+ _convertParameters(inputs: DoxityInput[]): Parameter[] {
+ const parameters = _.map(inputs, input => {
+ const parameter = {
+ name: input.name,
+ comment: input.description,
+ isOptional: false,
+ type: this._convertType(input.type),
+ };
+ return parameter;
+ });
+ return parameters;
+ },
+ _convertType(typeName: string): Type {
+ const type = {
+ name: typeName,
+ typeDocType: TypeDocTypes.Intrinsic,
+ };
+ return type;
+ },
+ _isMethod(abiDoc: DoxityAbiDoc) {
+ if (abiDoc.type !== AbiTypes.Function) {
+ return false;
+ }
+ const hasInputs = !_.isEmpty(abiDoc.inputs);
+ const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
+ const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
+ const isMethod = hasNamedOutputIfExists && !isNameAllCaps;
+ return isMethod;
+ },
+ _isProperty(abiDoc: DoxityAbiDoc) {
+ if (abiDoc.type !== AbiTypes.Function) {
+ return false;
+ }
+ const hasInputs = !_.isEmpty(abiDoc.inputs);
+ const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name);
+ const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase();
+ const isProperty = !hasNamedOutputIfExists || isNameAllCaps;
+ return isProperty;
+ },
+ _convertEventArgs(inputs: DoxityInput[]): EventArg[] {
+ const eventArgs = _.map(inputs, input => {
+ const eventArg = {
+ isIndexed: input.indexed,
+ name: input.name,
+ type: this._convertType(input.type),
+ };
+ return eventArg;
+ });
+ return eventArgs;
+ },
+};
diff --git a/packages/react-docs/src/ts/utils/typedoc_utils.ts b/packages/react-docs/src/ts/utils/typedoc_utils.ts
new file mode 100644
index 000000000..13798889a
--- /dev/null
+++ b/packages/react-docs/src/ts/utils/typedoc_utils.ts
@@ -0,0 +1,370 @@
+import * as _ from 'lodash';
+
+import { DocsInfo } from '../components/docs_info';
+import {
+ CustomType,
+ CustomTypeChild,
+ DocAgnosticFormat,
+ DocSection,
+ IndexSignature,
+ KindString,
+ Parameter,
+ Property,
+ SectionsMap,
+ Type,
+ TypeDocNode,
+ TypeDocType,
+ TypeParameter,
+ TypescriptMethod,
+} from '../types';
+import { utils } from '../utils/utils';
+
+export const typeDocUtils = {
+ isType(entity: TypeDocNode): boolean {
+ return (
+ entity.kindString === KindString.Interface ||
+ entity.kindString === KindString.Function ||
+ entity.kindString === KindString.TypeAlias ||
+ entity.kindString === KindString.Variable ||
+ entity.kindString === KindString.Enumeration
+ );
+ },
+ isMethod(entity: TypeDocNode): boolean {
+ return entity.kindString === KindString.Method;
+ },
+ isConstructor(entity: TypeDocNode): boolean {
+ return entity.kindString === KindString.Constructor;
+ },
+ isProperty(entity: TypeDocNode): boolean {
+ return entity.kindString === KindString.Property;
+ },
+ isPrivateOrProtectedProperty(propertyName: string): boolean {
+ return _.startsWith(propertyName, '_');
+ },
+ getModuleDefinitionsBySectionName(versionDocObj: TypeDocNode, configModulePaths: string[]): TypeDocNode[] {
+ const moduleDefinitions: TypeDocNode[] = [];
+ const jsonModules = versionDocObj.children;
+ _.each(jsonModules, jsonMod => {
+ _.each(configModulePaths, configModulePath => {
+ if (_.includes(configModulePath, jsonMod.name)) {
+ moduleDefinitions.push(jsonMod);
+ }
+ });
+ });
+ return moduleDefinitions;
+ },
+ convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat {
+ const subMenus = _.values(docsInfo.getMenu());
+ const orderedSectionNames = _.flatten(subMenus);
+ 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,
+ ];
+ }
+ }
+
+ // Since the `types.ts` file is the only file that does not export a module/class but
+ // instead has each type export itself, we do not need to go down two levels of nesting
+ // for it.
+ let entities;
+ let packageComment = '';
+ if (sectionName === docsInfo.sections.types) {
+ entities = packageDefinitionWithMergedChildren.children;
+ } else {
+ entities = packageDefinitionWithMergedChildren.children[0].children;
+ const commentObj = packageDefinitionWithMergedChildren.children[0].comment;
+ packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment;
+ }
+
+ const docSection = typeDocUtils._convertEntitiesToDocSection(entities, docsInfo, sectionName);
+ docSection.comment = packageComment;
+ docAgnosticFormat[sectionName] = docSection;
+ });
+ return docAgnosticFormat;
+ },
+ _convertEntitiesToDocSection(entities: TypeDocNode[], docsInfo: DocsInfo, sectionName: string) {
+ const docSection: DocSection = {
+ comment: '',
+ constructors: [],
+ methods: [],
+ properties: [],
+ types: [],
+ };
+
+ let isConstructor;
+ _.each(entities, entity => {
+ switch (entity.kindString) {
+ case KindString.Constructor:
+ isConstructor = true;
+ const constructor = typeDocUtils._convertMethod(
+ entity,
+ isConstructor,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.id,
+ );
+ docSection.constructors.push(constructor);
+ break;
+
+ case KindString.Method:
+ if (entity.flags.isPublic) {
+ isConstructor = false;
+ const method = typeDocUtils._convertMethod(
+ entity,
+ isConstructor,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.id,
+ );
+ docSection.methods.push(method);
+ }
+ break;
+
+ case KindString.Property:
+ if (!typeDocUtils.isPrivateOrProtectedProperty(entity.name)) {
+ const property = typeDocUtils._convertProperty(
+ entity,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.id,
+ );
+ docSection.properties.push(property);
+ }
+ break;
+
+ case KindString.Interface:
+ case KindString.Function:
+ case KindString.Variable:
+ case KindString.Enumeration:
+ case KindString.TypeAlias:
+ if (docsInfo.isPublicType(entity.name)) {
+ const customType = typeDocUtils._convertCustomType(
+ entity,
+ docsInfo.sections,
+ sectionName,
+ docsInfo.id,
+ );
+ docSection.types.push(customType);
+ }
+ break;
+
+ default:
+ throw utils.spawnSwitchErr('kindString', entity.kindString);
+ }
+ });
+ return docSection;
+ },
+ _convertCustomType(entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string): CustomType {
+ const typeIfExists = !_.isUndefined(entity.type)
+ ? typeDocUtils._convertType(entity.type, sections, sectionName, docId)
+ : undefined;
+ const isConstructor = false;
+ const methodIfExists = !_.isUndefined(entity.declaration)
+ ? 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 indexSignatureIfExists = doesIndexSignatureExist
+ ? typeDocUtils._convertIndexSignature(indexSignature, sections, sectionName, docId)
+ : undefined;
+ const commentIfExists =
+ !_.isUndefined(entity.comment) && !_.isUndefined(entity.comment.shortText)
+ ? entity.comment.shortText
+ : undefined;
+
+ const childrenIfExist = !_.isUndefined(entity.children)
+ ? _.map(entity.children, (child: TypeDocNode) => {
+ const childTypeIfExists = !_.isUndefined(child.type)
+ ? typeDocUtils._convertType(child.type, sections, sectionName, docId)
+ : undefined;
+ const c: CustomTypeChild = {
+ name: child.name,
+ type: childTypeIfExists,
+ defaultValue: child.defaultValue,
+ };
+ return c;
+ })
+ : undefined;
+
+ const customType = {
+ name: entity.name,
+ kindString: entity.kindString,
+ type: typeIfExists,
+ method: methodIfExists,
+ indexSignature: indexSignatureIfExists,
+ defaultValue: entity.defaultValue,
+ comment: commentIfExists,
+ children: childrenIfExist,
+ };
+ return customType;
+ },
+ _convertIndexSignature(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ docId: string,
+ ): IndexSignature {
+ const key = entity.parameters[0];
+ const indexSignature = {
+ keyName: key.name,
+ keyType: typeDocUtils._convertType(key.type, sections, sectionName, docId),
+ valueName: entity.type.name,
+ };
+ return indexSignature;
+ },
+ _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 property = {
+ name: entity.name,
+ type: typeDocUtils._convertType(entity.type, sections, sectionName, docId),
+ source: {
+ fileName: source.fileName,
+ line: source.line,
+ },
+ comment: commentIfExists,
+ };
+ return property;
+ },
+ _convertMethod(
+ entity: TypeDocNode,
+ isConstructor: boolean,
+ sections: SectionsMap,
+ sectionName: string,
+ docId: string,
+ ): TypescriptMethod {
+ const signature = entity.signatures[0];
+ const source = entity.sources[0];
+ 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);
+ });
+ const returnType = typeDocUtils._convertType(signature.type, sections, sectionName, docId);
+ const typeParameter = _.isUndefined(signature.typeParameter)
+ ? undefined
+ : typeDocUtils._convertTypeParameter(signature.typeParameter[0], sections, sectionName, docId);
+
+ const method = {
+ isConstructor,
+ isStatic,
+ name: signature.name,
+ comment: hasComment ? signature.comment.shortText : undefined,
+ returnComment: hasComment && signature.comment.returns ? signature.comment.returns : undefined,
+ source: {
+ fileName: source.fileName,
+ line: source.line,
+ },
+ callPath,
+ parameters,
+ returnType,
+ typeParameter,
+ };
+ return method;
+ },
+ _convertTypeParameter(
+ entity: TypeDocNode,
+ sections: SectionsMap,
+ sectionName: string,
+ docId: string,
+ ): TypeParameter {
+ const type = typeDocUtils._convertType(entity.type, sections, sectionName, docId);
+ const parameter = {
+ name: entity.name,
+ type,
+ };
+ return parameter;
+ },
+ _convertParameter(entity: TypeDocNode, sections: SectionsMap, sectionName: string, docId: string): Parameter {
+ let comment = '<No comment>';
+ if (entity.comment && entity.comment.shortText) {
+ comment = entity.comment.shortText;
+ } else if (entity.comment && entity.comment.text) {
+ comment = entity.comment.text;
+ }
+
+ const isOptional = !_.isUndefined(entity.flags.isOptional) ? entity.flags.isOptional : false;
+
+ const type = typeDocUtils._convertType(entity.type, sections, sectionName, docId);
+
+ const parameter = {
+ name: entity.name,
+ comment,
+ isOptional,
+ type,
+ };
+ return parameter;
+ },
+ _convertType(entity: TypeDocType, sections: SectionsMap, sectionName: string, docId: string): Type {
+ const typeArguments = _.map(entity.typeArguments, typeArgument => {
+ return typeDocUtils._convertType(typeArgument, sections, sectionName, docId);
+ });
+ const types = _.map(entity.types, t => {
+ return typeDocUtils._convertType(t, sections, sectionName, docId);
+ });
+
+ const isConstructor = false;
+ const methodIfExists = !_.isUndefined(entity.declaration)
+ ? typeDocUtils._convertMethod(entity.declaration, isConstructor, sections, sectionName, docId)
+ : undefined;
+
+ const elementTypeIfExists = !_.isUndefined(entity.elementType)
+ ? {
+ name: entity.elementType.name,
+ typeDocType: entity.elementType.type,
+ }
+ : undefined;
+
+ const type = {
+ name: entity.name,
+ value: entity.value,
+ typeDocType: entity.type,
+ typeArguments,
+ elementType: elementTypeIfExists,
+ types,
+ method: methodIfExists,
+ };
+ return type;
+ },
+};
diff --git a/packages/react-docs/src/ts/utils/utils.ts b/packages/react-docs/src/ts/utils/utils.ts
new file mode 100644
index 000000000..8e1a80a44
--- /dev/null
+++ b/packages/react-docs/src/ts/utils/utils.ts
@@ -0,0 +1,10 @@
+export const utils = {
+ consoleLog(message: string) {
+ /* tslint:disable */
+ console.log(message);
+ /* tslint:enable */
+ },
+ spawnSwitchErr(name: string, value: any) {
+ return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
+ },
+};
diff --git a/packages/react-docs/tsconfig.json b/packages/react-docs/tsconfig.json
new file mode 100644
index 000000000..6e71fb2e4
--- /dev/null
+++ b/packages/react-docs/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "./lib/",
+ "jsx": "react",
+ "baseUrl": "./",
+ "strictNullChecks": false,
+ "noImplicitThis": false,
+ "paths": {
+ "*": ["node_modules/@types/*", "*"]
+ }
+ },
+ "include": ["./src/ts/**/*"]
+}
diff --git a/packages/react-docs/tslint.json b/packages/react-docs/tslint.json
new file mode 100644
index 000000000..d6a5f5031
--- /dev/null
+++ b/packages/react-docs/tslint.json
@@ -0,0 +1,9 @@
+{
+ "extends": ["@0xproject/tslint-config"],
+ "rules": {
+ "no-implicit-dependencies": false,
+ "no-object-literal-type-assertion": false,
+ "completed-docs": false,
+ "prefer-function-over-method": false
+ }
+}