diff options
Diffstat (limited to 'packages/website/ts/utils/typedoc_utils.ts')
-rw-r--r-- | packages/website/ts/utils/typedoc_utils.ts | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/packages/website/ts/utils/typedoc_utils.ts b/packages/website/ts/utils/typedoc_utils.ts new file mode 100644 index 000000000..b3d0f7d90 --- /dev/null +++ b/packages/website/ts/utils/typedoc_utils.ts @@ -0,0 +1,356 @@ +import * as _ from 'lodash'; +import compareVersions = require('compare-versions'); +import {constants} from 'ts/utils/constants'; +import {utils} from 'ts/utils/utils'; +import { + TypeDocNode, + KindString, + ZeroExJsDocSections, + MenuSubsectionsBySection, + TypeDocType, + Type, + DocAgnosticFormat, + DocSection, + TypescriptMethod, + Parameter, + Property, + CustomType, + IndexSignature, + CustomTypeChild, + TypeParameter, + TypeDocTypes, +} from 'ts/types'; + +const TYPES_MODULE_PATH = '"src/types"'; + +export const sectionNameToPossibleModulePaths: {[name: string]: string[]} = { + [ZeroExJsDocSections.zeroEx]: ['"src/0x"'], + [ZeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'], + [ZeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'], + [ZeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'], + [ZeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'], + [ZeroExJsDocSections.proxy]: [ + '"src/contract_wrappers/proxy_wrapper"', + '"src/contract_wrappers/token_transfer_proxy_wrapper"', + ], + [ZeroExJsDocSections.types]: [TYPES_MODULE_PATH], +}; + +export const typeDocUtils = { + isType(entity: TypeDocNode): boolean { + return entity.kindString === KindString.Interface || + entity.kindString === KindString.Function || + entity.kindString === KindString['Type alias'] || + 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, '_'); + }, + isPublicType(typeName: string): boolean { + return _.includes(constants.public0xjsTypes, typeName); + }, + getModuleDefinitionBySectionNameIfExists(versionDocObj: TypeDocNode, sectionName: string): + TypeDocNode|undefined { + const possibleModulePathNames = sectionNameToPossibleModulePaths[sectionName]; + const modules = versionDocObj.children; + for (const mod of modules) { + if (_.includes(possibleModulePathNames, mod.name)) { + const moduleWithName = mod; + return moduleWithName; + } + } + return undefined; + }, + getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection { + const menuSubsectionsBySection = {} as MenuSubsectionsBySection; + if (_.isUndefined(docAgnosticFormat)) { + return menuSubsectionsBySection; + } + + const docSections = _.keys(ZeroExJsDocSections); + _.each(docSections, sectionName => { + const docSection = docAgnosticFormat[sectionName]; + if (_.isUndefined(docSection)) { + return; // no-op + } + + if (sectionName === ZeroExJsDocSections.types) { + const typeNames = _.map(docSection.types, t => t.name); + menuSubsectionsBySection[sectionName] = typeNames; + } else { + const methodNames = _.map(docSection.methods, m => m.name); + menuSubsectionsBySection[sectionName] = methodNames; + } + }); + return menuSubsectionsBySection; + }, + getFinal0xjsMenu(selectedVersion: string): {[section: string]: string[]} { + const finalMenu = _.cloneDeep(constants.menu0xjs); + finalMenu.contracts = _.filter(finalMenu.contracts, (contractName: string) => { + const versionIntroducedIfExists = constants.menuSubsectionToVersionWhenIntroduced[contractName]; + if (!_.isUndefined(versionIntroducedIfExists)) { + const existsInSelectedVersion = compareVersions(selectedVersion, + versionIntroducedIfExists) >= 0; + return existsInSelectedVersion; + } else { + return true; + } + }); + return finalMenu; + }, + convertToDocAgnosticFormat(typeDocJson: TypeDocNode): DocAgnosticFormat { + const subMenus = _.values(constants.menu0xjs); + const orderedSectionNames = _.flatten(subMenus); + const docAgnosticFormat: DocAgnosticFormat = {}; + _.each(orderedSectionNames, sectionName => { + const packageDefinitionIfExists = typeDocUtils.getModuleDefinitionBySectionNameIfExists( + typeDocJson, sectionName, + ); + if (_.isUndefined(packageDefinitionIfExists)) { + return; // no-op + } + + // 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 === ZeroExJsDocSections.types) { + entities = packageDefinitionIfExists.children; + } else { + entities = packageDefinitionIfExists.children[0].children; + const commentObj = packageDefinitionIfExists.children[0].comment; + packageComment = !_.isUndefined(commentObj) ? commentObj.shortText : packageComment; + } + + const docSection = typeDocUtils._convertEntitiesToDocSection(entities, sectionName); + docSection.comment = packageComment; + docAgnosticFormat[sectionName] = docSection; + }); + return docAgnosticFormat; + }, + _convertEntitiesToDocSection(entities: TypeDocNode[], 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, sectionName); + docSection.constructors.push(constructor); + break; + + case KindString.Method: + if (entity.flags.isPublic) { + isConstructor = false; + const method = typeDocUtils._convertMethod(entity, isConstructor, sectionName); + docSection.methods.push(method); + } + break; + + case KindString.Property: + if (!typeDocUtils.isPrivateOrProtectedProperty(entity.name)) { + const property = typeDocUtils._convertProperty(entity, sectionName); + docSection.properties.push(property); + } + break; + + case KindString.Interface: + case KindString.Function: + case KindString.Variable: + case KindString.Enumeration: + case KindString['Type alias']: + if (typeDocUtils.isPublicType(entity.name)) { + const customType = typeDocUtils._convertCustomType(entity, sectionName); + docSection.types.push(customType); + } + break; + + default: + throw utils.spawnSwitchErr('kindString', entity.kindString); + } + }); + return docSection; + }, + _convertCustomType(entity: TypeDocNode, sectionName: string): CustomType { + const typeIfExists = !_.isUndefined(entity.type) ? + typeDocUtils._convertType(entity.type, sectionName) : + undefined; + const isConstructor = false; + const methodIfExists = !_.isUndefined(entity.declaration) ? + typeDocUtils._convertMethod(entity.declaration, isConstructor, sectionName) : + undefined; + const indexSignatureIfExists = !_.isUndefined(entity.indexSignature) ? + typeDocUtils._convertIndexSignature(entity.indexSignature[0], sectionName) : + 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, sectionName) : + 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, sectionName: string): IndexSignature { + const key = entity.parameters[0]; + const indexSignature = { + keyName: key.name, + keyType: typeDocUtils._convertType(key.type, sectionName), + valueName: entity.type.name, + }; + return indexSignature; + }, + _convertProperty(entity: TypeDocNode, sectionName: 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, sectionName), + source: { + fileName: source.fileName, + line: source.line, + }, + comment: commentIfExists, + }; + return property; + }, + _convertMethod(entity: TypeDocNode, isConstructor: boolean, sectionName: 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; + + const topLevelInterface = isStatic ? 'ZeroEx.' : 'zeroEx.'; + // 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 = (sectionName !== ZeroExJsDocSections.zeroEx) ? + `${topLevelInterface}${sectionName}.` : + topLevelInterface; + callPath = isConstructor ? '' : callPath; + + const parameters = _.map(signature.parameters, param => { + return typeDocUtils._convertParameter(param, sectionName); + }); + const returnType = typeDocUtils._convertType(signature.type, sectionName); + const typeParameter = _.isUndefined(signature.typeParameter) ? + undefined : + typeDocUtils._convertTypeParameter(signature.typeParameter[0], sectionName); + + 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, sectionName: string): TypeParameter { + const type = typeDocUtils._convertType(entity.type, sectionName); + const parameter = { + name: entity.name, + type, + }; + return parameter; + }, + _convertParameter(entity: TypeDocNode, sectionName: 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, sectionName); + + const parameter = { + name: entity.name, + comment, + isOptional, + type, + }; + return parameter; + }, + _convertType(entity: TypeDocType, sectionName: string): Type { + const typeArguments = _.map(entity.typeArguments, typeArgument => { + return typeDocUtils._convertType(typeArgument, sectionName); + }); + const types = _.map(entity.types, t => { + return typeDocUtils._convertType(t, sectionName); + }); + + const isConstructor = false; + const methodIfExists = !_.isUndefined(entity.declaration) ? + typeDocUtils._convertMethod(entity.declaration, isConstructor, sectionName) : + 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; + }, +}; |