diff options
Diffstat (limited to 'packages/react-docs/src/utils')
-rw-r--r-- | packages/react-docs/src/utils/constants.ts | 9 | ||||
-rw-r--r-- | packages/react-docs/src/utils/doxity_utils.ts | 175 | ||||
-rw-r--r-- | packages/react-docs/src/utils/typedoc_utils.ts | 370 | ||||
-rw-r--r-- | packages/react-docs/src/utils/utils.ts | 10 |
4 files changed, 564 insertions, 0 deletions
diff --git a/packages/react-docs/src/utils/constants.ts b/packages/react-docs/src/utils/constants.ts new file mode 100644 index 000000000..c3c74fd11 --- /dev/null +++ b/packages/react-docs/src/utils/constants.ts @@ -0,0 +1,9 @@ +import { SupportedDocJson } from '../types'; + +export const constants = { + TYPES_SECTION_NAME: 'types', + TYPE_TO_SYNTAX: { + [SupportedDocJson.Doxity]: 'solidity', + [SupportedDocJson.TypeDoc]: 'typescript', + } as { [supportedDocType: string]: string }, +}; diff --git a/packages/react-docs/src/utils/doxity_utils.ts b/packages/react-docs/src/utils/doxity_utils.ts new file mode 100644 index 000000000..26dea6966 --- /dev/null +++ b/packages/react-docs/src/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/utils/typedoc_utils.ts b/packages/react-docs/src/utils/typedoc_utils.ts new file mode 100644 index 000000000..e4cea1e40 --- /dev/null +++ b/packages/react-docs/src/utils/typedoc_utils.ts @@ -0,0 +1,370 @@ +import * as _ from 'lodash'; + +import { DocsInfo } from '../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/utils/utils.ts b/packages/react-docs/src/utils/utils.ts new file mode 100644 index 000000000..8e1a80a44 --- /dev/null +++ b/packages/react-docs/src/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}`); + }, +}; |