From 775d1efd4607a4097704fe3c4f7ae1156b2c1a6f Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 23 Aug 2018 14:00:34 -0400 Subject: add package sol-doc --- packages/sol-doc/src/index.ts | 2 + packages/sol-doc/src/solidity_doc_format.ts | 5 +++ packages/sol-doc/src/solidity_doc_generator.ts | 60 ++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 packages/sol-doc/src/index.ts create mode 100644 packages/sol-doc/src/solidity_doc_format.ts create mode 100644 packages/sol-doc/src/solidity_doc_generator.ts (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/index.ts b/packages/sol-doc/src/index.ts new file mode 100644 index 000000000..f8f55c569 --- /dev/null +++ b/packages/sol-doc/src/index.ts @@ -0,0 +1,2 @@ +export { SolidityDocGenerator } from './solidity_doc_generator'; +export { SolidityDocFormat } from './solidity_doc_format'; diff --git a/packages/sol-doc/src/solidity_doc_format.ts b/packages/sol-doc/src/solidity_doc_format.ts new file mode 100644 index 000000000..edfd5c5b7 --- /dev/null +++ b/packages/sol-doc/src/solidity_doc_format.ts @@ -0,0 +1,5 @@ +import { DocAgnosticFormat, DocSection } from '@0xproject/react-docs'; + +export class SolidityDocFormat implements DocAgnosticFormat { + [sectionName: string]: DocSection; +} diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts new file mode 100644 index 000000000..c57a4779c --- /dev/null +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -0,0 +1,60 @@ +import * as _ from 'lodash'; + +import { MethodAbi } from 'ethereum-types'; + +import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; +import { logUtils } from '@0xproject/utils'; + +import { SolidityDocFormat } from './solidity_doc_format'; + +export class SolidityDocGenerator { + private readonly _compilerOptions: CompilerOptions; + constructor(contractsDir: string, artifactsDir: string) { + // instantiate sol-compiler, passing in options to say we want abi and devdoc + this._compilerOptions = { + contractsDir, + artifactsDir, + contracts: '*', + compilerSettings: { + outputSelection: { + ['*']: { + ['*']: ['abi', 'devdoc'], + }, + }, + }, + }; + } + /// run `contractsToCompile` through compiler, gathering output + public async generateAsync(contractsToCompile: string[]): Promise { + if (!_.isUndefined(contractsToCompile)) { + this._compilerOptions.contracts = contractsToCompile; + } + + const compiler = new Compiler(this._compilerOptions); + + const doc = new SolidityDocFormat(); + + const compilerOutputs = await compiler.getCompilerOutputsAsync(); + for (const compilerOutput of compilerOutputs) { + const solidityModules = _.keys(compilerOutput.contracts); + for (const solidityModule of solidityModules) { + const compiledSolidityModule = compilerOutput.contracts[solidityModule]; + + const contracts = _.keys(compiledSolidityModule); + for (const contract of contracts) { + const compiledContract = compiledSolidityModule[contract]; + + // TODO: modify typescript-typings/types/solc/index.d.ts... it doesn't currently support devdoc! + // tslint:disable-next-line:no-unnecessary-type-assertion tsc says abi[0] has no property `name` and won't compile without the `as`, but tslint says the `as` is unnecssary. + logUtils.log( + `TODO: extract data from ${contract}'s abi (eg ${ + (compiledContract.abi[0] as MethodAbi).name + }, etc) and devdoc outputs, and insert it into \`doc\``, + ); + } + } + } + + return doc; + } +} -- cgit From a1959df911741d0424a952fa4a63c5dcc1135524 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 24 Aug 2018 09:18:33 -0400 Subject: add devdoc support to solc typings, and use it --- packages/sol-doc/src/solidity_doc_generator.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index c57a4779c..fa3c40dcc 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -43,13 +43,19 @@ export class SolidityDocGenerator { const contracts = _.keys(compiledSolidityModule); for (const contract of contracts) { const compiledContract = compiledSolidityModule[contract]; + if (_.isUndefined(compiledContract.abi)) { + throw new Error('compiled contract did not contain ABI output.'); + } + if (_.isUndefined(compiledContract.devdoc)) { + throw new Error('compiled contract did not contain devdoc output.'); + } - // TODO: modify typescript-typings/types/solc/index.d.ts... it doesn't currently support devdoc! - // tslint:disable-next-line:no-unnecessary-type-assertion tsc says abi[0] has no property `name` and won't compile without the `as`, but tslint says the `as` is unnecssary. logUtils.log( - `TODO: extract data from ${contract}'s abi (eg ${ + `TODO: extract data from ${contract}'s abi (eg name, which is "${ (compiledContract.abi[0] as MethodAbi).name - }, etc) and devdoc outputs, and insert it into \`doc\``, + }", etc) and devdoc (eg title, which is "${ + compiledContract.devdoc.title + }") outputs, and insert it into \`doc\``, ); } } -- cgit From f429032eef007bafaed108171631f4b3d3233f6b Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 08:22:44 -0400 Subject: move shared doc types from react-docs to @0x/types --- packages/sol-doc/src/solidity_doc_format.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_format.ts b/packages/sol-doc/src/solidity_doc_format.ts index edfd5c5b7..e980f59f6 100644 --- a/packages/sol-doc/src/solidity_doc_format.ts +++ b/packages/sol-doc/src/solidity_doc_format.ts @@ -1,4 +1,4 @@ -import { DocAgnosticFormat, DocSection } from '@0xproject/react-docs'; +import { DocAgnosticFormat, DocSection } from '@0xproject/types'; export class SolidityDocFormat implements DocAgnosticFormat { [sectionName: string]: DocSection; -- cgit From f76c88c5491a9ec3a6b902b291dcfaf8ac64ecd5 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 11:24:41 -0400 Subject: remove unused constructor parameter --- packages/sol-doc/src/solidity_doc_generator.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index fa3c40dcc..a4eb403a8 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -9,11 +9,10 @@ import { SolidityDocFormat } from './solidity_doc_format'; export class SolidityDocGenerator { private readonly _compilerOptions: CompilerOptions; - constructor(contractsDir: string, artifactsDir: string) { + constructor(contractsDir: string) { // instantiate sol-compiler, passing in options to say we want abi and devdoc this._compilerOptions = { contractsDir, - artifactsDir, contracts: '*', compilerSettings: { outputSelection: { -- cgit From f55234b4a04a3a26e6eda05a5c77a6bd984571ba Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 11:30:43 -0400 Subject: comment public interface --- packages/sol-doc/src/solidity_doc_generator.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index a4eb403a8..f6bbf52ba 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -7,8 +7,16 @@ import { logUtils } from '@0xproject/utils'; import { SolidityDocFormat } from './solidity_doc_format'; +/** + * Compiles solidity files to both their ABI and devdoc outputs, and transforms + * those outputs into the types that feed into documentation generation tools. + */ export class SolidityDocGenerator { private readonly _compilerOptions: CompilerOptions; + /** + * Instantiate the generator. + * @param contractsDir the directory in which to find the contracts to be compiled + */ constructor(contractsDir: string) { // instantiate sol-compiler, passing in options to say we want abi and devdoc this._compilerOptions = { @@ -23,7 +31,11 @@ export class SolidityDocGenerator { }, }; } - /// run `contractsToCompile` through compiler, gathering output + /** + * Invoke the compiler and transform its outputs. + * @param contractsToCompile list of contracts for which to generate doc objects + * @return doc objects for use with documentation generation tools. + */ public async generateAsync(contractsToCompile: string[]): Promise { if (!_.isUndefined(contractsToCompile)) { this._compilerOptions.contracts = contractsToCompile; -- cgit From 297cbd996d7da0cb3232f47734290d0377693faf Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 11:49:54 -0400 Subject: remove unnecessary class SolidityDocFormat --- packages/sol-doc/src/index.ts | 1 - packages/sol-doc/src/solidity_doc_format.ts | 5 ----- packages/sol-doc/src/solidity_doc_generator.ts | 7 +++---- 3 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 packages/sol-doc/src/solidity_doc_format.ts (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/index.ts b/packages/sol-doc/src/index.ts index f8f55c569..dc88fae90 100644 --- a/packages/sol-doc/src/index.ts +++ b/packages/sol-doc/src/index.ts @@ -1,2 +1 @@ export { SolidityDocGenerator } from './solidity_doc_generator'; -export { SolidityDocFormat } from './solidity_doc_format'; diff --git a/packages/sol-doc/src/solidity_doc_format.ts b/packages/sol-doc/src/solidity_doc_format.ts deleted file mode 100644 index e980f59f6..000000000 --- a/packages/sol-doc/src/solidity_doc_format.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { DocAgnosticFormat, DocSection } from '@0xproject/types'; - -export class SolidityDocFormat implements DocAgnosticFormat { - [sectionName: string]: DocSection; -} diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index f6bbf52ba..aba7b5475 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -3,10 +3,9 @@ import * as _ from 'lodash'; import { MethodAbi } from 'ethereum-types'; import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; +import { DocAgnosticFormat } from '@0xproject/types'; import { logUtils } from '@0xproject/utils'; -import { SolidityDocFormat } from './solidity_doc_format'; - /** * Compiles solidity files to both their ABI and devdoc outputs, and transforms * those outputs into the types that feed into documentation generation tools. @@ -36,14 +35,14 @@ export class SolidityDocGenerator { * @param contractsToCompile list of contracts for which to generate doc objects * @return doc objects for use with documentation generation tools. */ - public async generateAsync(contractsToCompile: string[]): Promise { + public async generateAsync(contractsToCompile: string[]): Promise { if (!_.isUndefined(contractsToCompile)) { this._compilerOptions.contracts = contractsToCompile; } const compiler = new Compiler(this._compilerOptions); - const doc = new SolidityDocFormat(); + const doc: DocAgnosticFormat = {}; const compilerOutputs = await compiler.getCompilerOutputsAsync(); for (const compilerOutput of compilerOutputs) { -- cgit From b88a56e6b723016cf830511b2da503747e086f54 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 11:51:17 -0400 Subject: reduce proximity b/w instantiation and use of var --- packages/sol-doc/src/solidity_doc_generator.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index aba7b5475..856787e5f 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -40,10 +40,9 @@ export class SolidityDocGenerator { this._compilerOptions.contracts = contractsToCompile; } - const compiler = new Compiler(this._compilerOptions); - const doc: DocAgnosticFormat = {}; + const compiler = new Compiler(this._compilerOptions); const compilerOutputs = await compiler.getCompilerOutputsAsync(); for (const compilerOutput of compilerOutputs) { const solidityModules = _.keys(compilerOutput.contracts); -- cgit From fe7a62ba9a0d1647ea94683a0369b98e2a675f94 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 11:52:21 -0400 Subject: add named var to make `if` condition more readable --- packages/sol-doc/src/solidity_doc_generator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 856787e5f..bb2f4cea6 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -36,7 +36,8 @@ export class SolidityDocGenerator { * @return doc objects for use with documentation generation tools. */ public async generateAsync(contractsToCompile: string[]): Promise { - if (!_.isUndefined(contractsToCompile)) { + const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile); + if (shouldOverrideCatchAllContractsConfig) { this._compilerOptions.contracts = contractsToCompile; } -- cgit From 0e45497acbb0ad572e549ad81bb72b3b31d7860d Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 30 Aug 2018 12:02:02 -0400 Subject: rename variables --- packages/sol-doc/src/solidity_doc_generator.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index bb2f4cea6..95c89b2e5 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -46,13 +46,13 @@ export class SolidityDocGenerator { const compiler = new Compiler(this._compilerOptions); const compilerOutputs = await compiler.getCompilerOutputsAsync(); for (const compilerOutput of compilerOutputs) { - const solidityModules = _.keys(compilerOutput.contracts); - for (const solidityModule of solidityModules) { - const compiledSolidityModule = compilerOutput.contracts[solidityModule]; + const contractFileNames = _.keys(compilerOutput.contracts); + for (const contractFileName of contractFileNames) { + const contractNameToOutput = compilerOutput.contracts[contractFileName]; - const contracts = _.keys(compiledSolidityModule); - for (const contract of contracts) { - const compiledContract = compiledSolidityModule[contract]; + const contractNames = _.keys(contractNameToOutput); + for (const contractName of contractNames) { + const compiledContract = contractNameToOutput[contractName]; if (_.isUndefined(compiledContract.abi)) { throw new Error('compiled contract did not contain ABI output.'); } @@ -61,7 +61,7 @@ export class SolidityDocGenerator { } logUtils.log( - `TODO: extract data from ${contract}'s abi (eg name, which is "${ + `TODO: extract data from ${contractName}'s abi (eg name, which is "${ (compiledContract.abi[0] as MethodAbi).name }", etc) and devdoc (eg title, which is "${ compiledContract.devdoc.title -- cgit From 823b6c4d7df56e6bc517b72878fb1ff5823a5b6f Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 29 Aug 2018 11:01:04 -0400 Subject: transform solc's ABI output into doc types --- packages/sol-doc/src/index.ts | 2 +- packages/sol-doc/src/solidity_doc_generator.ts | 296 ++++++++++++++++++++----- 2 files changed, 237 insertions(+), 61 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/index.ts b/packages/sol-doc/src/index.ts index dc88fae90..03f3c9de6 100644 --- a/packages/sol-doc/src/index.ts +++ b/packages/sol-doc/src/index.ts @@ -1 +1 @@ -export { SolidityDocGenerator } from './solidity_doc_generator'; +export { generateSolDocAsync } from './solidity_doc_generator'; diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 95c89b2e5..312124ca1 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -1,76 +1,252 @@ import * as _ from 'lodash'; -import { MethodAbi } from 'ethereum-types'; +import { + AbiDefinition, + ConstructorAbi, + DataItem, + DevdocOutput, + EventAbi, + FallbackAbi, + MethodAbi, + StandardContractOutput, +} from 'ethereum-types'; import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; -import { DocAgnosticFormat } from '@0xproject/types'; +import { + DocAgnosticFormat, + DocSection, + Event, + EventArg, + Parameter, + SolidityMethod, + Type, + TypeDocTypes, +} from '@0xproject/types'; import { logUtils } from '@0xproject/utils'; /** - * Compiles solidity files to both their ABI and devdoc outputs, and transforms - * those outputs into the types that feed into documentation generation tools. + * Invoke the Solidity compiler and transform its ABI and devdoc outputs into + * the types that are used as input to documentation generation tools. + * @param contractsToCompile list of contracts for which to generate doc objects + * @param contractsDir the directory in which to find the `contractsToCompile` as well as their dependencies. + * @return doc object for use with documentation generation tools. */ -export class SolidityDocGenerator { - private readonly _compilerOptions: CompilerOptions; - /** - * Instantiate the generator. - * @param contractsDir the directory in which to find the contracts to be compiled - */ - constructor(contractsDir: string) { - // instantiate sol-compiler, passing in options to say we want abi and devdoc - this._compilerOptions = { - contractsDir, - contracts: '*', - compilerSettings: { - outputSelection: { - ['*']: { - ['*']: ['abi', 'devdoc'], - }, +export async function generateSolDocAsync( + contractsToCompile: string[], + contractsDir: string, +): Promise { + const doc: DocAgnosticFormat = {}; + + const compilerOptions = _makeCompilerOptions(contractsToCompile, contractsDir); + const compiler = new Compiler(compilerOptions); + const compilerOutputs = await compiler.getCompilerOutputsAsync(); + for (const compilerOutput of compilerOutputs) { + const contractFileNames = _.keys(compilerOutput.contracts); + for (const contractFileName of contractFileNames) { + const contractNameToOutput = compilerOutput.contracts[contractFileName]; + + const contractNames = _.keys(contractNameToOutput); + for (const contractName of contractNames) { + const compiledContract = contractNameToOutput[contractName]; + if (_.isUndefined(compiledContract.abi)) { + throw new Error('compiled contract did not contain ABI output'); + } + doc[contractName] = _genDocSection(compiledContract); + } + } + } + + return doc; +} + +function _makeCompilerOptions(contractsToCompile: string[], contractsDir: string): CompilerOptions { + const compilerOptions: CompilerOptions = { + contractsDir, + contracts: '*', + compilerSettings: { + outputSelection: { + ['*']: { + ['*']: ['abi', 'devdoc'], }, }, - }; + }, + }; + + const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile); + if (shouldOverrideCatchAllContractsConfig) { + compilerOptions.contracts = contractsToCompile; } - /** - * Invoke the compiler and transform its outputs. - * @param contractsToCompile list of contracts for which to generate doc objects - * @return doc objects for use with documentation generation tools. - */ - public async generateAsync(contractsToCompile: string[]): Promise { - const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile); - if (shouldOverrideCatchAllContractsConfig) { - this._compilerOptions.contracts = contractsToCompile; - } - const doc: DocAgnosticFormat = {}; - - const compiler = new Compiler(this._compilerOptions); - const compilerOutputs = await compiler.getCompilerOutputsAsync(); - for (const compilerOutput of compilerOutputs) { - const contractFileNames = _.keys(compilerOutput.contracts); - for (const contractFileName of contractFileNames) { - const contractNameToOutput = compilerOutput.contracts[contractFileName]; - - const contractNames = _.keys(contractNameToOutput); - for (const contractName of contractNames) { - const compiledContract = contractNameToOutput[contractName]; - if (_.isUndefined(compiledContract.abi)) { - throw new Error('compiled contract did not contain ABI output.'); - } - if (_.isUndefined(compiledContract.devdoc)) { - throw new Error('compiled contract did not contain devdoc output.'); - } - - logUtils.log( - `TODO: extract data from ${contractName}'s abi (eg name, which is "${ - (compiledContract.abi[0] as MethodAbi).name - }", etc) and devdoc (eg title, which is "${ - compiledContract.devdoc.title - }") outputs, and insert it into \`doc\``, - ); - } - } + return compilerOptions; +} + +function _genDocSection(compiledContract: StandardContractOutput): DocSection { + const docSection: DocSection = { + comment: _.isUndefined(compiledContract.devdoc) ? '' : compiledContract.devdoc.title, + constructors: [], + methods: [], + properties: [], + types: [], + functions: [], + events: [], + }; + + for (const abiDefinition of compiledContract.abi) { + switch (abiDefinition.type) { + case 'constructor': + docSection.constructors.push(_genConstructorDoc(abiDefinition, compiledContract.devdoc)); + break; + case 'event': + (docSection.events as Event[]).push(_genEventDoc(abiDefinition)); + // note that we're not sending devdoc to _genEventDoc(). + // that's because the type of the events array doesn't have any fields for documentation! + break; + case 'function': + docSection.methods.push(_genMethodDoc(abiDefinition, compiledContract.devdoc)); + break; + default: + throw new Error(`unknown and unsupported AbiDefinition type '${abiDefinition.type}'`); } + } + + return docSection; +} + +function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { + const { parameters, methodSignature } = _genMethodParamsDoc( + '', // TODO: update depending on how constructors are keyed in devdoc + abiDefinition.inputs, + devdocIfExists, + ); + + let comment; + // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and + // from that object extract the "details" (comment) property + comment = 'something from devdoc'; + + return { + isConstructor: true, + name: '', // sad we have to specify this + callPath: '', // TODO: wtf is this? + parameters, + returnType: { name: '', typeDocType: TypeDocTypes.Intrinsic }, // sad we have to specify this + isConstant: false, // constructors are non-const by their nature, right? + isPayable: abiDefinition.payable, + comment, + }; +} + +function _genMethodDoc( + abiDefinition: MethodAbi | FallbackAbi, + devdocIfExists: DevdocOutput | undefined, +): SolidityMethod { + const name = abiDefinition.type === 'fallback' ? '' : abiDefinition.name; - return doc; + const { parameters, methodSignature } = + abiDefinition.type === 'fallback' + ? { parameters: [], methodSignature: `${name}()` } + : _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); + + let comment; + // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and + // from that object extract the "details" (comment) property + comment = 'something from devdoc'; + + const returnType = + abiDefinition.type === 'fallback' + ? { name: '', typeDocType: TypeDocTypes.Intrinsic } + : _genMethodReturnTypeDoc(abiDefinition.outputs, methodSignature, devdocIfExists); + + const isConstant = abiDefinition.type === 'fallback' ? true : abiDefinition.constant; + // TODO: determine whether fallback functions are always constant, as coded just above + + return { + isConstructor: false, + name, + callPath: '', // TODO: wtf is this? + parameters, + returnType, + isConstant, + isPayable: abiDefinition.payable, + comment, + }; +} + +function _genEventDoc(abiDefinition: EventAbi): Event { + const eventDoc: Event = { + name: abiDefinition.name, + eventArgs: _genEventArgsDoc(abiDefinition.inputs, undefined), + }; + return eventDoc; +} + +function _genEventArgsDoc(args: DataItem[], devdocIfExists: DevdocOutput | undefined): EventArg[] { + const eventArgsDoc: EventArg[] = []; + + for (const arg of args) { + const name = arg.name; + + const type: Type = { + name: arg.type, + typeDocType: TypeDocTypes.Intrinsic, + }; + + const eventArgDoc: EventArg = { + isIndexed: false, // TODO: wtf is this? + name, + type, + }; + + eventArgsDoc.push(eventArgDoc); + } + return eventArgsDoc; +} + +/** + * Extract documentation for each method paramater from @param params. + * TODO: Then, use @param name, along with the types of the method + * parameters, to form a method signature. That signature is the key to + * the method documentation held in @param devdocIfExists. + */ +function _genMethodParamsDoc( + name: string, + params: DataItem[], + devdocIfExists: DevdocOutput | undefined, +): { parameters: Parameter[]; methodSignature: string } { + const parameters: Parameter[] = []; + for (const input of params) { + const parameter: Parameter = { + name: input.name, + comment: '', // TODO: get from devdoc. see comment below. + isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 + type: { name: input.type, typeDocType: TypeDocTypes.Intrinsic }, + }; + parameters.push(parameter); + } + // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and + // from that object extract the "details" (comment) property + return { parameters, methodSignature: '' }; +} + +function _genMethodReturnTypeDoc( + outputs: DataItem[], + methodSignature: string, + devdocIfExists: DevdocOutput | undefined, +): Type { + const methodReturnTypeDoc: Type = { + name: '', + typeDocType: TypeDocTypes.Intrinsic, + tupleElements: undefined, + }; + if (outputs.length > 1) { + methodReturnTypeDoc.typeDocType = TypeDocTypes.Tuple; + methodReturnTypeDoc.tupleElements = []; + for (const output of outputs) { + methodReturnTypeDoc.tupleElements.push({ name: output.name, typeDocType: TypeDocTypes.Intrinsic }); + } + } else if (outputs.length === 1) { + methodReturnTypeDoc.typeDocType = TypeDocTypes.Intrinsic; + methodReturnTypeDoc.name = outputs[0].name; } + return methodReturnTypeDoc; } -- cgit From 50b725c687c3968b0a495924700296ce6ce5bbec Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 31 Aug 2018 09:33:26 -0400 Subject: introduce named vars to clarify return statements --- packages/sol-doc/src/solidity_doc_generator.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 312124ca1..457c86c91 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -124,7 +124,7 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo // from that object extract the "details" (comment) property comment = 'something from devdoc'; - return { + const constructorDoc: SolidityMethod = { isConstructor: true, name: '', // sad we have to specify this callPath: '', // TODO: wtf is this? @@ -134,6 +134,8 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo isPayable: abiDefinition.payable, comment, }; + + return constructorDoc; } function _genMethodDoc( @@ -160,7 +162,7 @@ function _genMethodDoc( const isConstant = abiDefinition.type === 'fallback' ? true : abiDefinition.constant; // TODO: determine whether fallback functions are always constant, as coded just above - return { + const methodDoc: SolidityMethod = { isConstructor: false, name, callPath: '', // TODO: wtf is this? @@ -170,6 +172,7 @@ function _genMethodDoc( isPayable: abiDefinition.payable, comment, }; + return methodDoc; } function _genEventDoc(abiDefinition: EventAbi): Event { -- cgit From 327b4ba5546907f3c2b6fddd2a4d3d767d1f6767 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 31 Aug 2018 10:04:03 -0400 Subject: satisfy linter --- packages/sol-doc/src/solidity_doc_generator.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 457c86c91..debee5b87 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -1,7 +1,6 @@ import * as _ from 'lodash'; import { - AbiDefinition, ConstructorAbi, DataItem, DevdocOutput, @@ -22,7 +21,6 @@ import { Type, TypeDocTypes, } from '@0xproject/types'; -import { logUtils } from '@0xproject/utils'; /** * Invoke the Solidity compiler and transform its ABI and devdoc outputs into @@ -122,7 +120,7 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo let comment; // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and // from that object extract the "details" (comment) property - comment = 'something from devdoc'; + comment = `something from devdoc, using ${methodSignature} to find it`; const constructorDoc: SolidityMethod = { isConstructor: true, -- cgit From 3dc4eb4421d4f0b1294fcaaa4602245005f36e82 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 31 Aug 2018 15:28:12 -0400 Subject: rename 2 variables --- packages/sol-doc/src/solidity_doc_generator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index debee5b87..240fbbd75 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -211,16 +211,16 @@ function _genEventArgsDoc(args: DataItem[], devdocIfExists: DevdocOutput | undef */ function _genMethodParamsDoc( name: string, - params: DataItem[], + abiParams: DataItem[], devdocIfExists: DevdocOutput | undefined, ): { parameters: Parameter[]; methodSignature: string } { const parameters: Parameter[] = []; - for (const input of params) { + for (const abiParam of abiParams) { const parameter: Parameter = { - name: input.name, + name: abiParam.name, comment: '', // TODO: get from devdoc. see comment below. isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 - type: { name: input.type, typeDocType: TypeDocTypes.Intrinsic }, + type: { name: abiParam.type, typeDocType: TypeDocTypes.Intrinsic }, }; parameters.push(parameter); } -- cgit From 800dd5fb4f0ab21388440a46aef21cd0cf0801c3 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 31 Aug 2018 15:34:13 -0400 Subject: ammend generated docs with solc's devdoc output --- packages/sol-doc/src/solidity_doc_generator.ts | 61 +++++++++++++++++++------- 1 file changed, 46 insertions(+), 15 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 240fbbd75..a1ef32e79 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -117,10 +117,8 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo devdocIfExists, ); - let comment; - // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and - // from that object extract the "details" (comment) property - comment = `something from devdoc, using ${methodSignature} to find it`; + const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); + // TODO: figure out why devdoc'd constructors don't get output by solc const constructorDoc: SolidityMethod = { isConstructor: true, @@ -136,6 +134,26 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo return constructorDoc; } +function _devdocMethodDetailsIfExist( + methodSignature: string, + devdocIfExists: DevdocOutput | undefined, +): string | undefined { + let details; + if (!_.isUndefined(devdocIfExists)) { + const devdocMethodsIfExist = devdocIfExists.methods; + if (!_.isUndefined(devdocMethodsIfExist)) { + const devdocMethodIfExists = devdocMethodsIfExist[methodSignature]; + if (!_.isUndefined(devdocMethodIfExists)) { + const devdocMethodDetailsIfExist = devdocMethodIfExists.details; + if (!_.isUndefined(devdocMethodDetailsIfExist)) { + details = devdocMethodDetailsIfExist; + } + } + } + } + return details; +} + function _genMethodDoc( abiDefinition: MethodAbi | FallbackAbi, devdocIfExists: DevdocOutput | undefined, @@ -147,10 +165,7 @@ function _genMethodDoc( ? { parameters: [], methodSignature: `${name}()` } : _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); - let comment; - // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and - // from that object extract the "details" (comment) property - comment = 'something from devdoc'; + const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); const returnType = abiDefinition.type === 'fallback' @@ -205,9 +220,6 @@ function _genEventArgsDoc(args: DataItem[], devdocIfExists: DevdocOutput | undef /** * Extract documentation for each method paramater from @param params. - * TODO: Then, use @param name, along with the types of the method - * parameters, to form a method signature. That signature is the key to - * the method documentation held in @param devdocIfExists. */ function _genMethodParamsDoc( name: string, @@ -215,18 +227,37 @@ function _genMethodParamsDoc( devdocIfExists: DevdocOutput | undefined, ): { parameters: Parameter[]; methodSignature: string } { const parameters: Parameter[] = []; + let methodSignature = `${name}(`; + for (const abiParam of abiParams) { const parameter: Parameter = { name: abiParam.name, - comment: '', // TODO: get from devdoc. see comment below. + comment: '', isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 type: { name: abiParam.type, typeDocType: TypeDocTypes.Intrinsic }, }; parameters.push(parameter); + methodSignature = `${methodSignature}${abiParam.type},`; } - // TODO: use methodSignature as the key to abiEntry.devdoc.methods, and - // from that object extract the "details" (comment) property - return { parameters, methodSignature: '' }; + + if (methodSignature.slice(-1) === ',') { + methodSignature = methodSignature.slice(0, -1); + } + methodSignature += ')'; + + if (!_.isUndefined(devdocIfExists)) { + const devdocMethodIfExists = devdocIfExists.methods[methodSignature]; + if (!_.isUndefined(devdocMethodIfExists)) { + const devdocParamsIfExist = devdocMethodIfExists.params; + if (!_.isUndefined(devdocParamsIfExist)) { + for (const parameter of parameters) { + parameter.comment = devdocParamsIfExist[parameter.name]; + } + } + } + } + + return { parameters, methodSignature }; } function _genMethodReturnTypeDoc( -- cgit From d5f964b58e755ccf1e63a61a97da1c161cf22b22 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 13 Sep 2018 13:05:01 -0400 Subject: test w/&w/out passing in `contractsToCompile` --- packages/sol-doc/src/solidity_doc_generator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index a1ef32e79..513468803 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -30,12 +30,12 @@ import { * @return doc object for use with documentation generation tools. */ export async function generateSolDocAsync( - contractsToCompile: string[], contractsDir: string, + contractsToCompile?: string[], ): Promise { const doc: DocAgnosticFormat = {}; - const compilerOptions = _makeCompilerOptions(contractsToCompile, contractsDir); + const compilerOptions = _makeCompilerOptions(contractsDir, contractsToCompile); const compiler = new Compiler(compilerOptions); const compilerOutputs = await compiler.getCompilerOutputsAsync(); for (const compilerOutput of compilerOutputs) { @@ -57,7 +57,7 @@ export async function generateSolDocAsync( return doc; } -function _makeCompilerOptions(contractsToCompile: string[], contractsDir: string): CompilerOptions { +function _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[]): CompilerOptions { const compilerOptions: CompilerOptions = { contractsDir, contracts: '*', @@ -70,7 +70,7 @@ function _makeCompilerOptions(contractsToCompile: string[], contractsDir: string }, }; - const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile); + const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile) && contractsToCompile.length > 0; if (shouldOverrideCatchAllContractsConfig) { compilerOptions.contracts = contractsToCompile; } -- cgit From 5b07669bd0e128a92c8a71d797ac95d77337209b Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 17 Sep 2018 17:23:55 -0400 Subject: add and test support for fallback functions --- packages/sol-doc/src/solidity_doc_generator.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 513468803..3501e9a52 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -1,6 +1,7 @@ import * as _ from 'lodash'; import { + AbiDefinition, ConstructorAbi, DataItem, DevdocOutput, @@ -100,10 +101,13 @@ function _genDocSection(compiledContract: StandardContractOutput): DocSection { // that's because the type of the events array doesn't have any fields for documentation! break; case 'function': + case 'fallback': docSection.methods.push(_genMethodDoc(abiDefinition, compiledContract.devdoc)); break; default: - throw new Error(`unknown and unsupported AbiDefinition type '${abiDefinition.type}'`); + throw new Error( + `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, + ); } } -- cgit From 9201273939598a13ed00084acdb384403c36fcc4 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 17 Sep 2018 17:27:33 -0400 Subject: add and test support for return comment --- packages/sol-doc/src/solidity_doc_generator.ts | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 3501e9a52..e0dcb692a 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -176,6 +176,11 @@ function _genMethodDoc( ? { name: '', typeDocType: TypeDocTypes.Intrinsic } : _genMethodReturnTypeDoc(abiDefinition.outputs, methodSignature, devdocIfExists); + const returnComment = + _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) + ? undefined + : devdocIfExists.methods[methodSignature].return; + const isConstant = abiDefinition.type === 'fallback' ? true : abiDefinition.constant; // TODO: determine whether fallback functions are always constant, as coded just above @@ -185,6 +190,7 @@ function _genMethodDoc( callPath: '', // TODO: wtf is this? parameters, returnType, + returnComment, isConstant, isPayable: abiDefinition.payable, comment, -- cgit From 401ef2edef50a73ec387d95ac9998f9badb4b34a Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 17 Sep 2018 17:35:11 -0400 Subject: minorly tweak comments --- packages/sol-doc/src/solidity_doc_generator.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index e0dcb692a..6b561d8a6 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -122,7 +122,6 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo ); const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); - // TODO: figure out why devdoc'd constructors don't get output by solc const constructorDoc: SolidityMethod = { isConstructor: true, -- cgit From 91de15d50e77db615da900ffa7aba6d32f40a28e Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 17 Sep 2018 18:46:11 -0400 Subject: remove stale comments --- packages/sol-doc/src/solidity_doc_generator.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 6b561d8a6..8093020b9 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -115,21 +115,17 @@ function _genDocSection(compiledContract: StandardContractOutput): DocSection { } function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { - const { parameters, methodSignature } = _genMethodParamsDoc( - '', // TODO: update depending on how constructors are keyed in devdoc - abiDefinition.inputs, - devdocIfExists, - ); + const { parameters, methodSignature } = _genMethodParamsDoc('', abiDefinition.inputs, devdocIfExists); const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); const constructorDoc: SolidityMethod = { isConstructor: true, name: '', // sad we have to specify this - callPath: '', // TODO: wtf is this? + callPath: '', parameters, returnType: { name: '', typeDocType: TypeDocTypes.Intrinsic }, // sad we have to specify this - isConstant: false, // constructors are non-const by their nature, right? + isConstant: false, isPayable: abiDefinition.payable, comment, }; @@ -181,12 +177,11 @@ function _genMethodDoc( : devdocIfExists.methods[methodSignature].return; const isConstant = abiDefinition.type === 'fallback' ? true : abiDefinition.constant; - // TODO: determine whether fallback functions are always constant, as coded just above const methodDoc: SolidityMethod = { isConstructor: false, name, - callPath: '', // TODO: wtf is this? + callPath: '', parameters, returnType, returnComment, -- cgit From 4bab402e95fd2242ad3a472e2b5076c5026da50a Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 17 Sep 2018 19:02:48 -0400 Subject: doc constructors as returning a Reference... ...not an Intrinsic --- packages/sol-doc/src/solidity_doc_generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 8093020b9..6c67f921d 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -124,7 +124,7 @@ function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: Devdo name: '', // sad we have to specify this callPath: '', parameters, - returnType: { name: '', typeDocType: TypeDocTypes.Intrinsic }, // sad we have to specify this + returnType: { name: '', typeDocType: TypeDocTypes.Reference }, // sad we have to specify this isConstant: false, isPayable: abiDefinition.payable, comment, -- cgit From 30622631ff588b4b87a05e4ce3ca2906ed1edf9f Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 17 Sep 2018 19:03:44 -0400 Subject: use Array.join rather than string concatenation --- packages/sol-doc/src/solidity_doc_generator.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 6c67f921d..78c56e7fa 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -231,8 +231,6 @@ function _genMethodParamsDoc( devdocIfExists: DevdocOutput | undefined, ): { parameters: Parameter[]; methodSignature: string } { const parameters: Parameter[] = []; - let methodSignature = `${name}(`; - for (const abiParam of abiParams) { const parameter: Parameter = { name: abiParam.name, @@ -241,13 +239,13 @@ function _genMethodParamsDoc( type: { name: abiParam.type, typeDocType: TypeDocTypes.Intrinsic }, }; parameters.push(parameter); - methodSignature = `${methodSignature}${abiParam.type},`; } - if (methodSignature.slice(-1) === ',') { - methodSignature = methodSignature.slice(0, -1); - } - methodSignature += ')'; + const methodSignature = `${name}(${abiParams + .map(abiParam => { + return abiParam.type; + }) + .join(',')})`; if (!_.isUndefined(devdocIfExists)) { const devdocMethodIfExists = devdocIfExists.methods[methodSignature]; -- cgit From 3e5d427d4e873080f2a369db7f844cbadd610fcb Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 21 Sep 2018 10:56:17 -0400 Subject: feat: add sol-doc command-line interface modified sol-compiler to output progress/warning/error/etc messages to stderr rather than stdout, so that sol-doc can put its output (and nothing else) to stdout. for posterity, added sol-doc cli usage as npm scripts to package.json. --- packages/sol-doc/src/cli.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 packages/sol-doc/src/cli.ts (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts new file mode 100644 index 000000000..59fc185de --- /dev/null +++ b/packages/sol-doc/src/cli.ts @@ -0,0 +1,26 @@ +import 'source-map-support/register'; +import * as yargs from 'yargs'; + +import { generateSolDocAsync } from './solidity_doc_generator'; + +const JSON_TABWIDTH = 4; + +(async () => { + const argv = yargs + .option('contracts-dir', { + type: 'string', + description: 'path of contracts directory to compile', + }) + .option('contracts', { + type: 'string', + description: 'comma separated list of contracts to compile', + }) + .demandOption('contracts-dir') + .array('contracts') + .help().argv; + process.stdout.write( + JSON.stringify(await generateSolDocAsync(argv.contractsDir, argv.contracts), null, JSON_TABWIDTH), + ); +})().catch(err => { + throw err; +}); -- cgit From 37cb18e1daa8bf33b92b94b93851e91d50383c1e Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Sat, 22 Sep 2018 10:21:27 -0400 Subject: fix: rename variable --- packages/sol-doc/src/solidity_doc_generator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 78c56e7fa..2586cf91c 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -26,17 +26,17 @@ import { /** * Invoke the Solidity compiler and transform its ABI and devdoc outputs into * the types that are used as input to documentation generation tools. - * @param contractsToCompile list of contracts for which to generate doc objects + * @param contractsToDocument list of contracts for which to generate doc objects * @param contractsDir the directory in which to find the `contractsToCompile` as well as their dependencies. * @return doc object for use with documentation generation tools. */ export async function generateSolDocAsync( contractsDir: string, - contractsToCompile?: string[], + contractsToDocument?: string[], ): Promise { const doc: DocAgnosticFormat = {}; - const compilerOptions = _makeCompilerOptions(contractsDir, contractsToCompile); + const compilerOptions = _makeCompilerOptions(contractsDir, contractsToDocument); const compiler = new Compiler(compilerOptions); const compilerOutputs = await compiler.getCompilerOutputsAsync(); for (const compilerOutput of compilerOutputs) { -- cgit From 9f0dfb1e1a4c97e462cf298e0452be1d0fcf2216 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Sat, 22 Sep 2018 10:31:47 -0400 Subject: feat: make sol-doc only document what's requested if a list of contracts-to-document is passed into sol-doc, then the output should only contain documentation for the contracts requested. if an empty/undefined list is passed, then it should document all contracts that were found. --- packages/sol-doc/src/solidity_doc_generator.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 2586cf91c..074fb1f37 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -1,3 +1,5 @@ +import * as path from 'path'; + import * as _ from 'lodash'; import { @@ -34,8 +36,7 @@ export async function generateSolDocAsync( contractsDir: string, contractsToDocument?: string[], ): Promise { - const doc: DocAgnosticFormat = {}; - + const docWithDependencies: DocAgnosticFormat = {}; const compilerOptions = _makeCompilerOptions(contractsDir, contractsToDocument); const compiler = new Compiler(compilerOptions); const compilerOutputs = await compiler.getCompilerOutputsAsync(); @@ -50,11 +51,25 @@ export async function generateSolDocAsync( if (_.isUndefined(compiledContract.abi)) { throw new Error('compiled contract did not contain ABI output'); } - doc[contractName] = _genDocSection(compiledContract); + docWithDependencies[contractName] = _genDocSection(compiledContract); } } } + let doc: DocAgnosticFormat = {}; + if (_.isUndefined(contractsToDocument) || contractsToDocument.length === 0) { + doc = docWithDependencies; + } else { + for (const contractToDocument of contractsToDocument) { + const contractBasename = path.basename(contractToDocument); + const contractName = + contractBasename.lastIndexOf('.sol') === -1 + ? contractBasename + : contractBasename.substring(0, contractBasename.lastIndexOf('.sol')); + doc[contractName] = docWithDependencies[contractName]; + } + } + return doc; } -- cgit From 880cd6e440c76d819cdb04d2971375cf9e0ea97b Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 24 Sep 2018 10:21:12 -0400 Subject: fix: document return value's type, not its name --- packages/sol-doc/src/solidity_doc_generator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 074fb1f37..b2b5daff3 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -291,11 +291,11 @@ function _genMethodReturnTypeDoc( methodReturnTypeDoc.typeDocType = TypeDocTypes.Tuple; methodReturnTypeDoc.tupleElements = []; for (const output of outputs) { - methodReturnTypeDoc.tupleElements.push({ name: output.name, typeDocType: TypeDocTypes.Intrinsic }); + methodReturnTypeDoc.tupleElements.push({ name: output.type, typeDocType: TypeDocTypes.Intrinsic }); } } else if (outputs.length === 1) { methodReturnTypeDoc.typeDocType = TypeDocTypes.Intrinsic; - methodReturnTypeDoc.name = outputs[0].name; + methodReturnTypeDoc.name = outputs[0].type; } return methodReturnTypeDoc; } -- cgit From a9002558207883f66e3bb180415985d823f03772 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 24 Sep 2018 13:05:35 -0400 Subject: fix: document indexed event arguments --- packages/sol-doc/src/solidity_doc_generator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index b2b5daff3..0df4a4af1 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -8,6 +8,7 @@ import { DataItem, DevdocOutput, EventAbi, + EventParameter, FallbackAbi, MethodAbi, StandardContractOutput, @@ -215,7 +216,7 @@ function _genEventDoc(abiDefinition: EventAbi): Event { return eventDoc; } -function _genEventArgsDoc(args: DataItem[], devdocIfExists: DevdocOutput | undefined): EventArg[] { +function _genEventArgsDoc(args: EventParameter[], devdocIfExists: DevdocOutput | undefined): EventArg[] { const eventArgsDoc: EventArg[] = []; for (const arg of args) { @@ -227,7 +228,7 @@ function _genEventArgsDoc(args: DataItem[], devdocIfExists: DevdocOutput | undef }; const eventArgDoc: EventArg = { - isIndexed: false, // TODO: wtf is this? + isIndexed: arg.indexed, name, type, }; -- cgit From 57fca16d7b1dc61c060c90fa440b1dc947aefb93 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 24 Sep 2018 13:18:10 -0400 Subject: fix: use contract name for constructor method name and for its return value --- packages/sol-doc/src/solidity_doc_generator.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 0df4a4af1..b0c9ff314 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -52,7 +52,7 @@ export async function generateSolDocAsync( if (_.isUndefined(compiledContract.abi)) { throw new Error('compiled contract did not contain ABI output'); } - docWithDependencies[contractName] = _genDocSection(compiledContract); + docWithDependencies[contractName] = _genDocSection(compiledContract, contractName); } } } @@ -95,7 +95,7 @@ function _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[ return compilerOptions; } -function _genDocSection(compiledContract: StandardContractOutput): DocSection { +function _genDocSection(compiledContract: StandardContractOutput, contractName: string): DocSection { const docSection: DocSection = { comment: _.isUndefined(compiledContract.devdoc) ? '' : compiledContract.devdoc.title, constructors: [], @@ -109,7 +109,7 @@ function _genDocSection(compiledContract: StandardContractOutput): DocSection { for (const abiDefinition of compiledContract.abi) { switch (abiDefinition.type) { case 'constructor': - docSection.constructors.push(_genConstructorDoc(abiDefinition, compiledContract.devdoc)); + docSection.constructors.push(_genConstructorDoc(contractName, abiDefinition, compiledContract.devdoc)); break; case 'event': (docSection.events as Event[]).push(_genEventDoc(abiDefinition)); @@ -130,17 +130,21 @@ function _genDocSection(compiledContract: StandardContractOutput): DocSection { return docSection; } -function _genConstructorDoc(abiDefinition: ConstructorAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { +function _genConstructorDoc( + contractName: string, + abiDefinition: ConstructorAbi, + devdocIfExists: DevdocOutput | undefined, +): SolidityMethod { const { parameters, methodSignature } = _genMethodParamsDoc('', abiDefinition.inputs, devdocIfExists); const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); const constructorDoc: SolidityMethod = { isConstructor: true, - name: '', // sad we have to specify this + name: contractName, callPath: '', parameters, - returnType: { name: '', typeDocType: TypeDocTypes.Reference }, // sad we have to specify this + returnType: { name: contractName, typeDocType: TypeDocTypes.Reference }, // sad we have to specify this isConstant: false, isPayable: abiDefinition.payable, comment, -- cgit From ac5376a1ca29f8cba3c94f759654ea50d4947f84 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 25 Sep 2018 15:26:42 -0400 Subject: fix: rename JSON_TABWIDTH to JSON_TAB_WIDTH https://github.com/0xProject/0x-monorepo/pull/1004#discussion_r219977842 https://github.com/0xProject/0x-monorepo/pull/1004 --- packages/sol-doc/src/cli.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts index 59fc185de..1cdc35b64 100644 --- a/packages/sol-doc/src/cli.ts +++ b/packages/sol-doc/src/cli.ts @@ -3,7 +3,7 @@ import * as yargs from 'yargs'; import { generateSolDocAsync } from './solidity_doc_generator'; -const JSON_TABWIDTH = 4; +const JSON_TAB_WIDTH = 4; (async () => { const argv = yargs @@ -19,7 +19,7 @@ const JSON_TABWIDTH = 4; .array('contracts') .help().argv; process.stdout.write( - JSON.stringify(await generateSolDocAsync(argv.contractsDir, argv.contracts), null, JSON_TABWIDTH), + JSON.stringify(await generateSolDocAsync(argv.contractsDir, argv.contracts), null, JSON_TAB_WIDTH), ); })().catch(err => { throw err; -- cgit From 1db5ff617fe772d885e1cfd9ba67f645d8d4ee35 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 25 Sep 2018 15:29:09 -0400 Subject: fix: split line for readability https://github.com/0xProject/0x-monorepo/pull/1004#discussion_r219978696 https://github.com/0xProject/0x-monorepo/pull/1004 --- packages/sol-doc/src/cli.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts index 1cdc35b64..b29a89147 100644 --- a/packages/sol-doc/src/cli.ts +++ b/packages/sol-doc/src/cli.ts @@ -18,9 +18,8 @@ const JSON_TAB_WIDTH = 4; .demandOption('contracts-dir') .array('contracts') .help().argv; - process.stdout.write( - JSON.stringify(await generateSolDocAsync(argv.contractsDir, argv.contracts), null, JSON_TAB_WIDTH), - ); + const doc = await generateSolDocAsync(argv.contractsDir, argv.contracts); + process.stdout.write(JSON.stringify(doc, null, JSON_TAB_WIDTH)); })().catch(err => { throw err; }); -- cgit From 7fbfd9ccd935812796ce297bb81fd8a4b72aa734 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 25 Sep 2018 15:31:13 -0400 Subject: fix: print error and exit process; don't re-throw https://github.com/0xProject/0x-monorepo/pull/1004#discussion_r219979003 https://github.com/0xProject/0x-monorepo/pull/1004 --- packages/sol-doc/src/cli.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts index b29a89147..eb0f00bf6 100644 --- a/packages/sol-doc/src/cli.ts +++ b/packages/sol-doc/src/cli.ts @@ -1,6 +1,8 @@ import 'source-map-support/register'; import * as yargs from 'yargs'; +import { logUtils } from '@0xproject/utils'; + import { generateSolDocAsync } from './solidity_doc_generator'; const JSON_TAB_WIDTH = 4; @@ -21,5 +23,6 @@ const JSON_TAB_WIDTH = 4; const doc = await generateSolDocAsync(argv.contractsDir, argv.contracts); process.stdout.write(JSON.stringify(doc, null, JSON_TAB_WIDTH)); })().catch(err => { - throw err; + logUtils.warn(err); + process.exit(1); }); -- cgit From 2e7b218f4032cb213b947322e3e07ccb8709ca2b Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 25 Sep 2018 15:32:52 -0400 Subject: fix: comments https://github.com/0xProject/0x-monorepo/pull/1004#discussion_r219990314 https://github.com/0xProject/0x-monorepo/pull/1004#discussion_r220007992 https://github.com/0xProject/0x-monorepo/pull/1004 --- packages/sol-doc/src/solidity_doc_generator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index b0c9ff314..5ddf001a6 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -27,8 +27,8 @@ import { } from '@0xproject/types'; /** - * Invoke the Solidity compiler and transform its ABI and devdoc outputs into - * the types that are used as input to documentation generation tools. + * Invoke the Solidity compiler and transform its ABI and devdoc outputs into a + * JSON format easily consumed by documentation rendering tools. * @param contractsToDocument list of contracts for which to generate doc objects * @param contractsDir the directory in which to find the `contractsToCompile` as well as their dependencies. * @return doc object for use with documentation generation tools. @@ -243,7 +243,7 @@ function _genEventArgsDoc(args: EventParameter[], devdocIfExists: DevdocOutput | } /** - * Extract documentation for each method paramater from @param params. + * Extract documentation for each method parameter from @param params. */ function _genMethodParamsDoc( name: string, -- cgit From c55a41917851e66e3803883cf10b1e336e2dfc21 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 10:57:42 +0100 Subject: Split _genMethodDoc into it and _genFallbackDoc for clarity. Add isFallback boolean --- packages/sol-doc/src/solidity_doc_generator.ts | 54 +++++++++++++++----------- 1 file changed, 32 insertions(+), 22 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 5ddf001a6..d2fb5b083 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -117,8 +117,10 @@ function _genDocSection(compiledContract: StandardContractOutput, contractName: // that's because the type of the events array doesn't have any fields for documentation! break; case 'function': + docSection.methods.push(_genMethodDoc(abiDefinition as MethodAbi, compiledContract.devdoc)); + break; case 'fallback': - docSection.methods.push(_genMethodDoc(abiDefinition, compiledContract.devdoc)); + docSection.methods.push(_genFallbackDoc(abiDefinition as FallbackAbi, compiledContract.devdoc)); break; default: throw new Error( @@ -173,39 +175,47 @@ function _devdocMethodDetailsIfExist( return details; } -function _genMethodDoc( - abiDefinition: MethodAbi | FallbackAbi, - devdocIfExists: DevdocOutput | undefined, -): SolidityMethod { - const name = abiDefinition.type === 'fallback' ? '' : abiDefinition.name; - - const { parameters, methodSignature } = - abiDefinition.type === 'fallback' - ? { parameters: [], methodSignature: `${name}()` } - : _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); - +function _genFallbackDoc(abiDefinition: FallbackAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { + const methodSignature = `${name}()`; const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); - const returnType = - abiDefinition.type === 'fallback' - ? { name: '', typeDocType: TypeDocTypes.Intrinsic } - : _genMethodReturnTypeDoc(abiDefinition.outputs, methodSignature, devdocIfExists); - const returnComment = _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) ? undefined : devdocIfExists.methods[methodSignature].return; - const isConstant = abiDefinition.type === 'fallback' ? true : abiDefinition.constant; + const methodDoc: SolidityMethod = { + isConstructor: false, + name: '', + callPath: '', + parameters: [], + returnType: { name: 'void', typeDocType: TypeDocTypes.Intrinsic }, + returnComment, + isConstant: true, + isPayable: abiDefinition.payable, + isFallback: true, + comment, + }; + return methodDoc; +} + +function _genMethodDoc(abiDefinition: MethodAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { + const { parameters, methodSignature } = _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); + const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); + const returnType = _genMethodReturnTypeDoc(abiDefinition.outputs, methodSignature, devdocIfExists); + const returnComment = + _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) + ? undefined + : devdocIfExists.methods[methodSignature].return; const methodDoc: SolidityMethod = { isConstructor: false, - name, + name: abiDefinition.name, callPath: '', parameters, returnType, returnComment, - isConstant, + isConstant: abiDefinition.constant, isPayable: abiDefinition.payable, comment, }; @@ -254,7 +264,7 @@ function _genMethodParamsDoc( for (const abiParam of abiParams) { const parameter: Parameter = { name: abiParam.name, - comment: '', + comment: '', isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 type: { name: abiParam.type, typeDocType: TypeDocTypes.Intrinsic }, }; @@ -288,7 +298,7 @@ function _genMethodReturnTypeDoc( devdocIfExists: DevdocOutput | undefined, ): Type { const methodReturnTypeDoc: Type = { - name: '', + name: 'void', typeDocType: TypeDocTypes.Intrinsic, tupleElements: undefined, }; -- cgit From d4077ae970205f0e41519f95ec5a7ba29cdd105b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 23:01:12 +0100 Subject: Fix linter --- packages/sol-doc/src/solidity_doc_generator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index d2fb5b083..b7b428259 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -117,10 +117,10 @@ function _genDocSection(compiledContract: StandardContractOutput, contractName: // that's because the type of the events array doesn't have any fields for documentation! break; case 'function': - docSection.methods.push(_genMethodDoc(abiDefinition as MethodAbi, compiledContract.devdoc)); + docSection.methods.push(_genMethodDoc(abiDefinition, compiledContract.devdoc)); break; case 'fallback': - docSection.methods.push(_genFallbackDoc(abiDefinition as FallbackAbi, compiledContract.devdoc)); + docSection.methods.push(_genFallbackDoc(abiDefinition, compiledContract.devdoc)); break; default: throw new Error( -- cgit From 957af23a6422d4010bfa6e3e60e1a8e161a50216 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 23:02:12 +0100 Subject: Include structs section so that we can render struct params/return values properly with a popover --- packages/sol-doc/src/solidity_doc_generator.ts | 235 ++++++++++++++++++++++--- 1 file changed, 210 insertions(+), 25 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index b7b428259..c70f54d5f 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -1,7 +1,5 @@ import * as path from 'path'; -import * as _ from 'lodash'; - import { AbiDefinition, ConstructorAbi, @@ -13,9 +11,13 @@ import { MethodAbi, StandardContractOutput, } from 'ethereum-types'; +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; import { + CustomType, + CustomTypeChild, DocAgnosticFormat, DocSection, Event, @@ -26,6 +28,19 @@ import { TypeDocTypes, } from '@0xproject/types'; +// Unforunately, the only way to currently retrieve the declared structs with Solidity contracts +// is to tease it out of the params/return values included in it's ABI. These structures do +// not include the structs actual name, so we needed a mapping to assign the proper name to a +// struct. If the name is not in this mapping, the structs name will default to the param/return value +// name. +const customTypeHashToName: { [hash: string]: string } = { + '52d4a768701076c7bac06e386e430883975eb398732eccba797fd09dd064a60e': 'Order', + '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResult', + c22239cf0d29df1e6cf1be54f21692a8c0b3a48b9367540d4ffff4608b331ce9: 'OrderInfo', + c21e9ff31a30941c22e1cb43752114bb467c34dea58947f98966c9030fc8e4a9: 'TraderInfo', + '07c2bddc165e0b5005e6244dd4a9771fa61c78c4f42abd687d57567b0768136c': 'MatchedFillResult', +}; + /** * Invoke the Solidity compiler and transform its ABI and devdoc outputs into a * JSON format easily consumed by documentation rendering tools. @@ -41,6 +56,7 @@ export async function generateSolDocAsync( const compilerOptions = _makeCompilerOptions(contractsDir, contractsToDocument); const compiler = new Compiler(compilerOptions); const compilerOutputs = await compiler.getCompilerOutputsAsync(); + let structs: CustomType[] = []; for (const compilerOutput of compilerOutputs) { const contractFileNames = _.keys(compilerOutput.contracts); for (const contractFileName of contractFileNames) { @@ -53,9 +69,12 @@ export async function generateSolDocAsync( throw new Error('compiled contract did not contain ABI output'); } docWithDependencies[contractName] = _genDocSection(compiledContract, contractName); + structs = [...structs, ..._extractStructs(compiledContract)]; } } } + structs = _dedupStructs(structs); + structs = _overwriteStructNames(structs); let doc: DocAgnosticFormat = {}; if (_.isUndefined(contractsToDocument) || contractsToDocument.length === 0) { @@ -71,6 +90,16 @@ export async function generateSolDocAsync( } } + doc.structs = { + comment: '', + constructors: [], + methods: [], + properties: [], + types: structs, + functions: [], + events: [], + }; + return doc; } @@ -95,6 +124,33 @@ function _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[ return compilerOptions; } +function _extractStructs(compiledContract: StandardContractOutput): CustomType[] { + let customTypes: CustomType[] = []; + for (const abiDefinition of compiledContract.abi) { + switch (abiDefinition.type) { + case 'constructor': { + const types = _getStructsAsCustomTypes(abiDefinition); + customTypes = [...customTypes, ...types]; + break; + } + case 'function': { + const types = _getStructsAsCustomTypes(abiDefinition); + customTypes = [...customTypes, ...types]; + break; + } + case 'event': + case 'fallback': + // No types exist + break; + default: + throw new Error( + `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, + ); + } + } + return customTypes; +} + function _genDocSection(compiledContract: StandardContractOutput, contractName: string): DocSection { const docSection: DocSection = { comment: _.isUndefined(compiledContract.devdoc) ? '' : compiledContract.devdoc.title, @@ -176,7 +232,7 @@ function _devdocMethodDetailsIfExist( } function _genFallbackDoc(abiDefinition: FallbackAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { - const methodSignature = `${name}()`; + const methodSignature = `()`; const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); const returnComment = @@ -186,7 +242,7 @@ function _genFallbackDoc(abiDefinition: FallbackAbi, devdocIfExists: DevdocOutpu const methodDoc: SolidityMethod = { isConstructor: false, - name: '', + name: 'fallback', callPath: '', parameters: [], returnType: { name: 'void', typeDocType: TypeDocTypes.Intrinsic }, @@ -194,23 +250,32 @@ function _genFallbackDoc(abiDefinition: FallbackAbi, devdocIfExists: DevdocOutpu isConstant: true, isPayable: abiDefinition.payable, isFallback: true, - comment, + comment: _.isEmpty(comment) + ? 'The default fallback function. It is executed on a call to the contract if none of the other functions match the given function identifier (or if no data was supplied at all).' + : comment, }; return methodDoc; } function _genMethodDoc(abiDefinition: MethodAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { + const name = abiDefinition.name; const { parameters, methodSignature } = _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); - const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); - const returnType = _genMethodReturnTypeDoc(abiDefinition.outputs, methodSignature, devdocIfExists); + const devDocComment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); + const returnType = _genMethodReturnTypeDoc(abiDefinition.outputs); const returnComment = _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) ? undefined : devdocIfExists.methods[methodSignature].return; + const hasNoNamedParameters = _.isUndefined(_.find(parameters, p => !_.isEmpty(p.name))); + const isGeneratedGetter = hasNoNamedParameters; + const comment = + _.isEmpty(devDocComment) && isGeneratedGetter + ? `This is an auto-generated accessor method of the '${name}' contract instance variable.` + : devDocComment; const methodDoc: SolidityMethod = { isConstructor: false, - name: abiDefinition.name, + name, callPath: '', parameters, returnType, @@ -262,11 +327,13 @@ function _genMethodParamsDoc( ): { parameters: Parameter[]; methodSignature: string } { const parameters: Parameter[] = []; for (const abiParam of abiParams) { + const type = _getTypeFromDataItem(abiParam); + const parameter: Parameter = { name: abiParam.name, comment: '', isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 - type: { name: abiParam.type, typeDocType: TypeDocTypes.Intrinsic }, + type, }; parameters.push(parameter); } @@ -292,25 +359,143 @@ function _genMethodParamsDoc( return { parameters, methodSignature }; } -function _genMethodReturnTypeDoc( - outputs: DataItem[], - methodSignature: string, - devdocIfExists: DevdocOutput | undefined, -): Type { - const methodReturnTypeDoc: Type = { - name: 'void', - typeDocType: TypeDocTypes.Intrinsic, - tupleElements: undefined, - }; +function _genMethodReturnTypeDoc(outputs: DataItem[]): Type { if (outputs.length > 1) { - methodReturnTypeDoc.typeDocType = TypeDocTypes.Tuple; - methodReturnTypeDoc.tupleElements = []; + const type: Type = { + name: '', + typeDocType: TypeDocTypes.Tuple, + tupleElements: [], + }; for (const output of outputs) { - methodReturnTypeDoc.tupleElements.push({ name: output.type, typeDocType: TypeDocTypes.Intrinsic }); + const tupleType = _getTypeFromDataItem(output); + (type.tupleElements as Type[]).push(tupleType); } + return type; } else if (outputs.length === 1) { - methodReturnTypeDoc.typeDocType = TypeDocTypes.Intrinsic; - methodReturnTypeDoc.name = outputs[0].type; + const output = outputs[0]; + const type = _getTypeFromDataItem(output); + return type; + } else { + const type: Type = { + name: 'void', + typeDocType: TypeDocTypes.Intrinsic, + }; + return type; + } +} + +function _capitalize(text: string): string { + return `${text.charAt(0).toUpperCase()}${text.slice(1)}`; +} + +function _dedupStructs(customTypes: CustomType[]): CustomType[] { + const uniqueCustomTypes: CustomType[] = []; + const seenTypes: { [hash: string]: boolean } = {}; + _.each(customTypes, customType => { + const hash = _generateCustomTypeHash(customType); + if (!seenTypes[hash]) { + uniqueCustomTypes.push(customType); + seenTypes[hash] = true; + } + }); + return uniqueCustomTypes; +} + +function _overwriteStructNames(customTypes: CustomType[]): CustomType[] { + const localCustomTypes = _.cloneDeep(customTypes); + _.each(localCustomTypes, customType => { + const hash = _generateCustomTypeHash(customType); + if (!_.isUndefined(customTypeHashToName[hash])) { + customType.name = customTypeHashToName[hash]; + } + }); + return localCustomTypes; +} + +function _generateCustomTypeHash(customType: CustomType): string { + const customTypeWithoutName = _.cloneDeep(customType); + delete customTypeWithoutName.name; + const customTypeWithoutNameStr = JSON.stringify(customTypeWithoutName); + const hash = ethUtil.sha256(customTypeWithoutNameStr).toString('hex'); + return hash; +} + +function _getStructsAsCustomTypes(abiDefinition: AbiDefinition): CustomType[] { + const customTypes: CustomType[] = []; + if (!_.isUndefined((abiDefinition as any).inputs)) { + const methodOrConstructorAbi = abiDefinition as MethodAbi | ConstructorAbi; + _.each(methodOrConstructorAbi.inputs, input => { + if (!_.isUndefined(input.components)) { + const customType = _getCustomTypeFromDataItem(input); + customTypes.push(customType); + } + }); + } + if (!_.isUndefined((abiDefinition as any).outputs)) { + const methodAbi = abiDefinition as MethodAbi; + _.each(methodAbi.outputs, output => { + if (!_.isUndefined(output.components)) { + const customType = _getCustomTypeFromDataItem(output); + customTypes.push(customType); + } + }); + } + return customTypes; +} + +function _getCustomTypeFromDataItem(inputOrOutput: DataItem): CustomType { + const customType: CustomType = { + name: _.capitalize(inputOrOutput.name), + kindString: 'Interface', + children: [], + }; + _.each(inputOrOutput.components, (component: DataItem) => { + const childType = _getTypeFromDataItem(component); + const customTypeChild = { + name: component.name, + type: childType, + }; + // (fabio): Not sure why this type casting is necessary. Seems TS doesn't + // deduce that `customType.children` cannot be undefined anymore after being + // set to `[]` above. + (customType.children as CustomTypeChild[]).push(customTypeChild); + }); + return customType; +} + +function _getNameFromDataItemIfExists(dataItem: DataItem): string | undefined { + if (_.isUndefined(dataItem.components)) { + return undefined; + } + const customType = _getCustomTypeFromDataItem(dataItem); + const hash = _generateCustomTypeHash(customType); + if (_.isUndefined(customTypeHashToName[hash])) { + return undefined; + } + return customTypeHashToName[hash]; +} + +function _getTypeFromDataItem(dataItem: DataItem): Type { + const typeDocType = !_.isUndefined(dataItem.components) ? TypeDocTypes.Reference : TypeDocTypes.Intrinsic; + let typeName: string; + if (typeDocType === TypeDocTypes.Reference) { + const nameIfExists = _getNameFromDataItemIfExists(dataItem); + typeName = _.isUndefined(nameIfExists) ? _capitalize(dataItem.name) : nameIfExists; + } else { + typeName = dataItem.type; + } + + const isArrayType = _.endsWith(dataItem.type, '[]'); + let type: Type; + if (isArrayType) { + typeName = typeDocType === TypeDocTypes.Intrinsic ? typeName.slice(0, -2) : typeName; + type = { + elementType: { name: typeName, typeDocType }, + typeDocType: TypeDocTypes.Array, + name: '', + }; + } else { + type = { name: typeName, typeDocType }; } - return methodReturnTypeDoc; + return type; } -- cgit From 95e84aae49d86ed8edf286bc8c95214f5b2456c7 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 23:18:32 +0100 Subject: Improve comment --- packages/sol-doc/src/solidity_doc_generator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index c70f54d5f..1e9c7d5d7 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -28,11 +28,11 @@ import { TypeDocTypes, } from '@0xproject/types'; -// Unforunately, the only way to currently retrieve the declared structs with Solidity contracts -// is to tease it out of the params/return values included in it's ABI. These structures do -// not include the structs actual name, so we needed a mapping to assign the proper name to a +// Unforunately, the only way to currently retrieve the declared structs within Solidity contracts +// is to tease them out of the params/return values included in the ABI. These structures do +// not include the structs actual name, so we need a mapping to assign the proper name to a // struct. If the name is not in this mapping, the structs name will default to the param/return value -// name. +// name (which mostly coincide). const customTypeHashToName: { [hash: string]: string } = { '52d4a768701076c7bac06e386e430883975eb398732eccba797fd09dd064a60e': 'Order', '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResult', -- cgit From e0ff3484cf169162f3a339b6cb779fa2b562ad73 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 23:20:21 +0100 Subject: Improve switch case --- packages/sol-doc/src/solidity_doc_generator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 1e9c7d5d7..09e78aef2 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -127,15 +127,14 @@ function _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[ function _extractStructs(compiledContract: StandardContractOutput): CustomType[] { let customTypes: CustomType[] = []; for (const abiDefinition of compiledContract.abi) { + let types: CustomType[] = []; switch (abiDefinition.type) { case 'constructor': { - const types = _getStructsAsCustomTypes(abiDefinition); - customTypes = [...customTypes, ...types]; + types = _getStructsAsCustomTypes(abiDefinition); break; } case 'function': { - const types = _getStructsAsCustomTypes(abiDefinition); - customTypes = [...customTypes, ...types]; + types = _getStructsAsCustomTypes(abiDefinition); break; } case 'event': @@ -147,6 +146,7 @@ function _extractStructs(compiledContract: StandardContractOutput): CustomType[] `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, ); } + customTypes = [...customTypes, ...types]; } return customTypes; } -- cgit From b2ff7bda02c02a950eb2cc656eaa5780d7b71b5b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 23:24:05 +0100 Subject: Explain cast to any --- packages/sol-doc/src/solidity_doc_generator.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 09e78aef2..d441b9a23 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -422,6 +422,8 @@ function _generateCustomTypeHash(customType: CustomType): string { function _getStructsAsCustomTypes(abiDefinition: AbiDefinition): CustomType[] { const customTypes: CustomType[] = []; + // We cast to `any` here because we do not know yet if this type of abiDefinition contains + // an `input` key if (!_.isUndefined((abiDefinition as any).inputs)) { const methodOrConstructorAbi = abiDefinition as MethodAbi | ConstructorAbi; _.each(methodOrConstructorAbi.inputs, input => { -- cgit From 092b184f4534a42d36d30d2d10b34b47255d2ee3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 23:30:11 +0100 Subject: Fix linter --- packages/sol-doc/src/solidity_doc_generator.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index d441b9a23..362fe12c3 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -490,6 +490,7 @@ function _getTypeFromDataItem(dataItem: DataItem): Type { const isArrayType = _.endsWith(dataItem.type, '[]'); let type: Type; if (isArrayType) { + // tslint:disable-next-line:custom-no-magic-numbers typeName = typeDocType === TypeDocTypes.Intrinsic ? typeName.slice(0, -2) : typeName; type = { elementType: { name: typeName, typeDocType }, @@ -501,3 +502,4 @@ function _getTypeFromDataItem(dataItem: DataItem): Type { } return type; } +// tslint:disable:max-file-line-count -- cgit From 144561c53be3df1e03cd46de23cec4710056907a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 28 Sep 2018 10:28:57 +0100 Subject: Fix missing devdoc comments in output by fixing improper encoding of methodSignatures --- packages/sol-doc/src/solidity_doc_generator.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 362fe12c3..09137c4cf 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -340,7 +340,16 @@ function _genMethodParamsDoc( const methodSignature = `${name}(${abiParams .map(abiParam => { - return abiParam.type; + if (!_.startsWith(abiParam.type, 'tuple')) { + return abiParam.type; + } else { + // Need to expand tuples: + // E.g: fillOrder(tuple,uint256,bytes) -> fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes) + const isArray = _.endsWith(abiParam.type, '[]'); + const expandedTypes = _.map(abiParam.components, c => c.type); + const type = `(${expandedTypes.join(',')})${isArray ? '[]' : ''}`; + return type; + } }) .join(',')})`; -- cgit From 28f5cd06418e52a80fb784dbed75120d76b4204b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 28 Sep 2018 10:30:24 +0100 Subject: Only add Stucts section if there are any --- packages/sol-doc/src/solidity_doc_generator.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts index 09137c4cf..424577411 100644 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ b/packages/sol-doc/src/solidity_doc_generator.ts @@ -90,15 +90,17 @@ export async function generateSolDocAsync( } } - doc.structs = { - comment: '', - constructors: [], - methods: [], - properties: [], - types: structs, - functions: [], - events: [], - }; + if (structs.length > 0) { + doc.structs = { + comment: '', + constructors: [], + methods: [], + properties: [], + types: structs, + functions: [], + events: [], + }; + } return doc; } -- cgit From 9ae60d0abe0ea32eb9dbfc48a9168d1996c674a0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 28 Sep 2018 17:20:24 +0100 Subject: Export class instead of function --- packages/sol-doc/src/cli.ts | 17 +- packages/sol-doc/src/index.ts | 2 +- packages/sol-doc/src/sol_doc.ts | 500 ++++++++++++++++++++++++ packages/sol-doc/src/solidity_doc_generator.ts | 516 ------------------------- 4 files changed, 516 insertions(+), 519 deletions(-) create mode 100644 packages/sol-doc/src/sol_doc.ts delete mode 100644 packages/sol-doc/src/solidity_doc_generator.ts (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts index eb0f00bf6..04956510e 100644 --- a/packages/sol-doc/src/cli.ts +++ b/packages/sol-doc/src/cli.ts @@ -3,7 +3,7 @@ import * as yargs from 'yargs'; import { logUtils } from '@0xproject/utils'; -import { generateSolDocAsync } from './solidity_doc_generator'; +import { SolDoc } from './sol_doc'; const JSON_TAB_WIDTH = 4; @@ -20,7 +20,20 @@ const JSON_TAB_WIDTH = 4; .demandOption('contracts-dir') .array('contracts') .help().argv; - const doc = await generateSolDocAsync(argv.contractsDir, argv.contracts); + // Unforunately, the only way to currently retrieve the declared structs within Solidity contracts + // is to tease them out of the params/return values included in the ABI. These structures do + // not include the structs actual name, so we need a mapping to assign the proper name to a + // struct. If the name is not in this mapping, the structs name will default to the param/return value + // name (which mostly coincide). + const customTypeHashToName: { [hash: string]: string } = { + '52d4a768701076c7bac06e386e430883975eb398732eccba797fd09dd064a60e': 'Order', + '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResult', + c22239cf0d29df1e6cf1be54f21692a8c0b3a48b9367540d4ffff4608b331ce9: 'OrderInfo', + c21e9ff31a30941c22e1cb43752114bb467c34dea58947f98966c9030fc8e4a9: 'TraderInfo', + '07c2bddc165e0b5005e6244dd4a9771fa61c78c4f42abd687d57567b0768136c': 'MatchedFillResults', + }; + const solDoc = new SolDoc(); + const doc = await solDoc.generateSolDocAsync(argv.contractsDir, argv.contracts, customTypeHashToName); process.stdout.write(JSON.stringify(doc, null, JSON_TAB_WIDTH)); })().catch(err => { logUtils.warn(err); diff --git a/packages/sol-doc/src/index.ts b/packages/sol-doc/src/index.ts index 03f3c9de6..521668cc8 100644 --- a/packages/sol-doc/src/index.ts +++ b/packages/sol-doc/src/index.ts @@ -1 +1 @@ -export { generateSolDocAsync } from './solidity_doc_generator'; +export { SolDoc } from './sol_doc'; diff --git a/packages/sol-doc/src/sol_doc.ts b/packages/sol-doc/src/sol_doc.ts new file mode 100644 index 000000000..4b7cbe77c --- /dev/null +++ b/packages/sol-doc/src/sol_doc.ts @@ -0,0 +1,500 @@ +import * as path from 'path'; + +import { + AbiDefinition, + ConstructorAbi, + DataItem, + DevdocOutput, + EventAbi, + EventParameter, + FallbackAbi, + MethodAbi, + StandardContractOutput, +} from 'ethereum-types'; +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; + +import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; +import { + CustomType, + CustomTypeChild, + DocAgnosticFormat, + DocSection, + Event, + EventArg, + ObjectMap, + Parameter, + SolidityMethod, + Type, + TypeDocTypes, +} from '@0xproject/types'; + +export class SolDoc { + private _customTypeHashToName: ObjectMap | undefined; + private static _genEventDoc(abiDefinition: EventAbi): Event { + const eventDoc: Event = { + name: abiDefinition.name, + eventArgs: SolDoc._genEventArgsDoc(abiDefinition.inputs), + }; + return eventDoc; + } + private static _devdocMethodDetailsIfExist( + methodSignature: string, + devdocIfExists: DevdocOutput | undefined, + ): string | undefined { + let details; + if (!_.isUndefined(devdocIfExists)) { + const devdocMethodsIfExist = devdocIfExists.methods; + if (!_.isUndefined(devdocMethodsIfExist)) { + const devdocMethodIfExists = devdocMethodsIfExist[methodSignature]; + if (!_.isUndefined(devdocMethodIfExists)) { + const devdocMethodDetailsIfExist = devdocMethodIfExists.details; + if (!_.isUndefined(devdocMethodDetailsIfExist)) { + details = devdocMethodDetailsIfExist; + } + } + } + } + return details; + } + private static _genFallbackDoc( + abiDefinition: FallbackAbi, + devdocIfExists: DevdocOutput | undefined, + ): SolidityMethod { + const methodSignature = `()`; + const comment = SolDoc._devdocMethodDetailsIfExist(methodSignature, devdocIfExists); + + const returnComment = + _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) + ? undefined + : devdocIfExists.methods[methodSignature].return; + + const methodDoc: SolidityMethod = { + isConstructor: false, + name: 'fallback', + callPath: '', + parameters: [], + returnType: { name: 'void', typeDocType: TypeDocTypes.Intrinsic }, + returnComment, + isConstant: true, + isPayable: abiDefinition.payable, + isFallback: true, + comment: _.isEmpty(comment) + ? 'The fallback function. It is executed on a call to the contract if none of the other functions match the given public identifier (or if no data was supplied at all).' + : comment, + }; + return methodDoc; + } + private static _genEventArgsDoc(args: EventParameter[]): EventArg[] { + const eventArgsDoc: EventArg[] = []; + + for (const arg of args) { + const name = arg.name; + + const type: Type = { + name: arg.type, + typeDocType: TypeDocTypes.Intrinsic, + }; + + const eventArgDoc: EventArg = { + isIndexed: arg.indexed, + name, + type, + }; + + eventArgsDoc.push(eventArgDoc); + } + return eventArgsDoc; + } + private static _dedupStructs(customTypes: CustomType[]): CustomType[] { + const uniqueCustomTypes: CustomType[] = []; + const seenTypes: { [hash: string]: boolean } = {}; + _.each(customTypes, customType => { + const hash = SolDoc._generateCustomTypeHash(customType); + if (!seenTypes[hash]) { + uniqueCustomTypes.push(customType); + seenTypes[hash] = true; + } + }); + return uniqueCustomTypes; + } + private static _capitalize(text: string): string { + return `${text.charAt(0).toUpperCase()}${text.slice(1)}`; + } + private static _generateCustomTypeHash(customType: CustomType): string { + const customTypeWithoutName = _.cloneDeep(customType); + delete customTypeWithoutName.name; + const customTypeWithoutNameStr = JSON.stringify(customTypeWithoutName); + const hash = ethUtil.sha256(customTypeWithoutNameStr).toString('hex'); + return hash; + } + private static _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[]): CompilerOptions { + const compilerOptions: CompilerOptions = { + contractsDir, + contracts: '*', + compilerSettings: { + outputSelection: { + ['*']: { + ['*']: ['abi', 'devdoc'], + }, + }, + }, + }; + + const shouldOverrideCatchAllContractsConfig = + !_.isUndefined(contractsToCompile) && contractsToCompile.length > 0; + if (shouldOverrideCatchAllContractsConfig) { + compilerOptions.contracts = contractsToCompile; + } + + return compilerOptions; + } + /** + * Invoke the Solidity compiler and transform its ABI and devdoc outputs into a + * JSON format easily consumed by documentation rendering tools. + * @param contractsToDocument list of contracts for which to generate doc objects + * @param contractsDir the directory in which to find the `contractsToCompile` as well as their dependencies. + * @return doc object for use with documentation generation tools. + */ + public async generateSolDocAsync( + contractsDir: string, + contractsToDocument?: string[], + customTypeHashToName?: ObjectMap, + ): Promise { + this._customTypeHashToName = customTypeHashToName; + const docWithDependencies: DocAgnosticFormat = {}; + const compilerOptions = SolDoc._makeCompilerOptions(contractsDir, contractsToDocument); + const compiler = new Compiler(compilerOptions); + const compilerOutputs = await compiler.getCompilerOutputsAsync(); + let structs: CustomType[] = []; + for (const compilerOutput of compilerOutputs) { + const contractFileNames = _.keys(compilerOutput.contracts); + for (const contractFileName of contractFileNames) { + const contractNameToOutput = compilerOutput.contracts[contractFileName]; + + const contractNames = _.keys(contractNameToOutput); + for (const contractName of contractNames) { + const compiledContract = contractNameToOutput[contractName]; + if (_.isUndefined(compiledContract.abi)) { + throw new Error('compiled contract did not contain ABI output'); + } + docWithDependencies[contractName] = this._genDocSection(compiledContract, contractName); + structs = [...structs, ...this._extractStructs(compiledContract)]; + } + } + } + structs = SolDoc._dedupStructs(structs); + structs = this._overwriteStructNames(structs); + + let doc: DocAgnosticFormat = {}; + if (_.isUndefined(contractsToDocument) || contractsToDocument.length === 0) { + doc = docWithDependencies; + } else { + for (const contractToDocument of contractsToDocument) { + const contractBasename = path.basename(contractToDocument); + const contractName = + contractBasename.lastIndexOf('.sol') === -1 + ? contractBasename + : contractBasename.substring(0, contractBasename.lastIndexOf('.sol')); + doc[contractName] = docWithDependencies[contractName]; + } + } + + if (structs.length > 0) { + doc.structs = { + comment: '', + constructors: [], + methods: [], + properties: [], + types: structs, + functions: [], + events: [], + }; + } + + delete this._customTypeHashToName; // Clean up instance state + return doc; + } + private _getCustomTypeFromDataItem(inputOrOutput: DataItem): CustomType { + const customType: CustomType = { + name: _.capitalize(inputOrOutput.name), + kindString: 'Interface', + children: [], + }; + _.each(inputOrOutput.components, (component: DataItem) => { + const childType = this._getTypeFromDataItem(component); + const customTypeChild = { + name: component.name, + type: childType, + }; + // (fabio): Not sure why this type casting is necessary. Seems TS doesn't + // deduce that `customType.children` cannot be undefined anymore after being + // set to `[]` above. + (customType.children as CustomTypeChild[]).push(customTypeChild); + }); + return customType; + } + private _getNameFromDataItemIfExists(dataItem: DataItem): string | undefined { + if (_.isUndefined(dataItem.components)) { + return undefined; + } + const customType = this._getCustomTypeFromDataItem(dataItem); + const hash = SolDoc._generateCustomTypeHash(customType); + if (_.isUndefined(this._customTypeHashToName) || _.isUndefined(this._customTypeHashToName[hash])) { + return undefined; + } + return this._customTypeHashToName[hash]; + } + private _getTypeFromDataItem(dataItem: DataItem): Type { + const typeDocType = !_.isUndefined(dataItem.components) ? TypeDocTypes.Reference : TypeDocTypes.Intrinsic; + let typeName: string; + if (typeDocType === TypeDocTypes.Reference) { + const nameIfExists = this._getNameFromDataItemIfExists(dataItem); + typeName = _.isUndefined(nameIfExists) ? SolDoc._capitalize(dataItem.name) : nameIfExists; + } else { + typeName = dataItem.type; + } + + const isArrayType = _.endsWith(dataItem.type, '[]'); + let type: Type; + if (isArrayType) { + // tslint:disable-next-line:custom-no-magic-numbers + typeName = typeDocType === TypeDocTypes.Intrinsic ? typeName.slice(0, -2) : typeName; + type = { + elementType: { name: typeName, typeDocType }, + typeDocType: TypeDocTypes.Array, + name: '', + }; + } else { + type = { name: typeName, typeDocType }; + } + return type; + } + private _overwriteStructNames(customTypes: CustomType[], customTypeHashToName?: ObjectMap): CustomType[] { + if (_.isUndefined(customTypeHashToName)) { + return customTypes; + } + const localCustomTypes = _.cloneDeep(customTypes); + _.each(localCustomTypes, customType => { + const hash = SolDoc._generateCustomTypeHash(customType); + if (!_.isUndefined(this._customTypeHashToName) && !_.isUndefined(this._customTypeHashToName[hash])) { + customType.name = this._customTypeHashToName[hash]; + } + }); + return localCustomTypes; + } + private _extractStructs(compiledContract: StandardContractOutput): CustomType[] { + let customTypes: CustomType[] = []; + for (const abiDefinition of compiledContract.abi) { + let types: CustomType[] = []; + switch (abiDefinition.type) { + case 'constructor': { + types = this._getStructsAsCustomTypes(abiDefinition); + break; + } + case 'function': { + types = this._getStructsAsCustomTypes(abiDefinition); + break; + } + case 'event': + case 'fallback': + // No types exist + break; + default: + throw new Error( + `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, + ); + } + customTypes = [...customTypes, ...types]; + } + return customTypes; + } + private _genDocSection(compiledContract: StandardContractOutput, contractName: string): DocSection { + const docSection: DocSection = { + comment: _.isUndefined(compiledContract.devdoc) ? '' : compiledContract.devdoc.title, + constructors: [], + methods: [], + properties: [], + types: [], + functions: [], + events: [], + }; + + for (const abiDefinition of compiledContract.abi) { + switch (abiDefinition.type) { + case 'constructor': + docSection.constructors.push( + this._genConstructorDoc(contractName, abiDefinition, compiledContract.devdoc), + ); + break; + case 'event': + (docSection.events as Event[]).push(SolDoc._genEventDoc(abiDefinition)); + // note that we're not sending devdoc to this._genEventDoc(). + // that's because the type of the events array doesn't have any fields for documentation! + break; + case 'function': + docSection.methods.push(this._genMethodDoc(abiDefinition, compiledContract.devdoc)); + break; + case 'fallback': + docSection.methods.push(SolDoc._genFallbackDoc(abiDefinition, compiledContract.devdoc)); + break; + default: + throw new Error( + `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, + ); + } + } + + return docSection; + } + private _genConstructorDoc( + contractName: string, + abiDefinition: ConstructorAbi, + devdocIfExists: DevdocOutput | undefined, + ): SolidityMethod { + const { parameters, methodSignature } = this._genMethodParamsDoc('', abiDefinition.inputs, devdocIfExists); + + const comment = SolDoc._devdocMethodDetailsIfExist(methodSignature, devdocIfExists); + + const constructorDoc: SolidityMethod = { + isConstructor: true, + name: contractName, + callPath: '', + parameters, + returnType: { name: contractName, typeDocType: TypeDocTypes.Reference }, // sad we have to specify this + isConstant: false, + isPayable: abiDefinition.payable, + comment, + }; + + return constructorDoc; + } + private _genMethodDoc(abiDefinition: MethodAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { + const name = abiDefinition.name; + const { parameters, methodSignature } = this._genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); + const devDocComment = SolDoc._devdocMethodDetailsIfExist(methodSignature, devdocIfExists); + const returnType = this._genMethodReturnTypeDoc(abiDefinition.outputs); + const returnComment = + _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) + ? undefined + : devdocIfExists.methods[methodSignature].return; + + const hasNoNamedParameters = _.isUndefined(_.find(parameters, p => !_.isEmpty(p.name))); + const isGeneratedGetter = hasNoNamedParameters; + const comment = + _.isEmpty(devDocComment) && isGeneratedGetter + ? `This is an auto-generated accessor method of the '${name}' contract instance variable.` + : devDocComment; + const methodDoc: SolidityMethod = { + isConstructor: false, + name, + callPath: '', + parameters, + returnType, + returnComment, + isConstant: abiDefinition.constant, + isPayable: abiDefinition.payable, + comment, + }; + return methodDoc; + } + /** + * Extract documentation for each method parameter from @param params. + */ + private _genMethodParamsDoc( + name: string, + abiParams: DataItem[], + devdocIfExists: DevdocOutput | undefined, + ): { parameters: Parameter[]; methodSignature: string } { + const parameters: Parameter[] = []; + for (const abiParam of abiParams) { + const type = this._getTypeFromDataItem(abiParam); + + const parameter: Parameter = { + name: abiParam.name, + comment: '', + isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 + type, + }; + parameters.push(parameter); + } + + const methodSignature = `${name}(${abiParams + .map(abiParam => { + if (!_.startsWith(abiParam.type, 'tuple')) { + return abiParam.type; + } else { + // Need to expand tuples: + // E.g: fillOrder(tuple,uint256,bytes) -> fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes) + const isArray = _.endsWith(abiParam.type, '[]'); + const expandedTypes = _.map(abiParam.components, c => c.type); + const type = `(${expandedTypes.join(',')})${isArray ? '[]' : ''}`; + return type; + } + }) + .join(',')})`; + + if (!_.isUndefined(devdocIfExists)) { + const devdocMethodIfExists = devdocIfExists.methods[methodSignature]; + if (!_.isUndefined(devdocMethodIfExists)) { + const devdocParamsIfExist = devdocMethodIfExists.params; + if (!_.isUndefined(devdocParamsIfExist)) { + for (const parameter of parameters) { + parameter.comment = devdocParamsIfExist[parameter.name]; + } + } + } + } + + return { parameters, methodSignature }; + } + private _genMethodReturnTypeDoc(outputs: DataItem[]): Type { + let type: Type; + if (outputs.length > 1) { + type = { + name: '', + typeDocType: TypeDocTypes.Tuple, + tupleElements: [], + }; + for (const output of outputs) { + const tupleType = this._getTypeFromDataItem(output); + (type.tupleElements as Type[]).push(tupleType); + } + return type; + } else if (outputs.length === 1) { + const output = outputs[0]; + type = this._getTypeFromDataItem(output); + } else { + type = { + name: 'void', + typeDocType: TypeDocTypes.Intrinsic, + }; + } + return type; + } + private _getStructsAsCustomTypes(abiDefinition: AbiDefinition): CustomType[] { + const customTypes: CustomType[] = []; + // We cast to `any` here because we do not know yet if this type of abiDefinition contains + // an `input` key + if (!_.isUndefined((abiDefinition as any).inputs)) { + const methodOrConstructorAbi = abiDefinition as MethodAbi | ConstructorAbi; + _.each(methodOrConstructorAbi.inputs, input => { + if (!_.isUndefined(input.components)) { + const customType = this._getCustomTypeFromDataItem(input); + customTypes.push(customType); + } + }); + } + if (!_.isUndefined((abiDefinition as any).outputs)) { + const methodAbi = abiDefinition as MethodAbi; + _.each(methodAbi.outputs, output => { + if (!_.isUndefined(output.components)) { + const customType = this._getCustomTypeFromDataItem(output); + customTypes.push(customType); + } + }); + } + return customTypes; + } +} +// tslint:disable:max-file-line-count diff --git a/packages/sol-doc/src/solidity_doc_generator.ts b/packages/sol-doc/src/solidity_doc_generator.ts deleted file mode 100644 index 424577411..000000000 --- a/packages/sol-doc/src/solidity_doc_generator.ts +++ /dev/null @@ -1,516 +0,0 @@ -import * as path from 'path'; - -import { - AbiDefinition, - ConstructorAbi, - DataItem, - DevdocOutput, - EventAbi, - EventParameter, - FallbackAbi, - MethodAbi, - StandardContractOutput, -} from 'ethereum-types'; -import ethUtil = require('ethereumjs-util'); -import * as _ from 'lodash'; - -import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; -import { - CustomType, - CustomTypeChild, - DocAgnosticFormat, - DocSection, - Event, - EventArg, - Parameter, - SolidityMethod, - Type, - TypeDocTypes, -} from '@0xproject/types'; - -// Unforunately, the only way to currently retrieve the declared structs within Solidity contracts -// is to tease them out of the params/return values included in the ABI. These structures do -// not include the structs actual name, so we need a mapping to assign the proper name to a -// struct. If the name is not in this mapping, the structs name will default to the param/return value -// name (which mostly coincide). -const customTypeHashToName: { [hash: string]: string } = { - '52d4a768701076c7bac06e386e430883975eb398732eccba797fd09dd064a60e': 'Order', - '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResult', - c22239cf0d29df1e6cf1be54f21692a8c0b3a48b9367540d4ffff4608b331ce9: 'OrderInfo', - c21e9ff31a30941c22e1cb43752114bb467c34dea58947f98966c9030fc8e4a9: 'TraderInfo', - '07c2bddc165e0b5005e6244dd4a9771fa61c78c4f42abd687d57567b0768136c': 'MatchedFillResult', -}; - -/** - * Invoke the Solidity compiler and transform its ABI and devdoc outputs into a - * JSON format easily consumed by documentation rendering tools. - * @param contractsToDocument list of contracts for which to generate doc objects - * @param contractsDir the directory in which to find the `contractsToCompile` as well as their dependencies. - * @return doc object for use with documentation generation tools. - */ -export async function generateSolDocAsync( - contractsDir: string, - contractsToDocument?: string[], -): Promise { - const docWithDependencies: DocAgnosticFormat = {}; - const compilerOptions = _makeCompilerOptions(contractsDir, contractsToDocument); - const compiler = new Compiler(compilerOptions); - const compilerOutputs = await compiler.getCompilerOutputsAsync(); - let structs: CustomType[] = []; - for (const compilerOutput of compilerOutputs) { - const contractFileNames = _.keys(compilerOutput.contracts); - for (const contractFileName of contractFileNames) { - const contractNameToOutput = compilerOutput.contracts[contractFileName]; - - const contractNames = _.keys(contractNameToOutput); - for (const contractName of contractNames) { - const compiledContract = contractNameToOutput[contractName]; - if (_.isUndefined(compiledContract.abi)) { - throw new Error('compiled contract did not contain ABI output'); - } - docWithDependencies[contractName] = _genDocSection(compiledContract, contractName); - structs = [...structs, ..._extractStructs(compiledContract)]; - } - } - } - structs = _dedupStructs(structs); - structs = _overwriteStructNames(structs); - - let doc: DocAgnosticFormat = {}; - if (_.isUndefined(contractsToDocument) || contractsToDocument.length === 0) { - doc = docWithDependencies; - } else { - for (const contractToDocument of contractsToDocument) { - const contractBasename = path.basename(contractToDocument); - const contractName = - contractBasename.lastIndexOf('.sol') === -1 - ? contractBasename - : contractBasename.substring(0, contractBasename.lastIndexOf('.sol')); - doc[contractName] = docWithDependencies[contractName]; - } - } - - if (structs.length > 0) { - doc.structs = { - comment: '', - constructors: [], - methods: [], - properties: [], - types: structs, - functions: [], - events: [], - }; - } - - return doc; -} - -function _makeCompilerOptions(contractsDir: string, contractsToCompile?: string[]): CompilerOptions { - const compilerOptions: CompilerOptions = { - contractsDir, - contracts: '*', - compilerSettings: { - outputSelection: { - ['*']: { - ['*']: ['abi', 'devdoc'], - }, - }, - }, - }; - - const shouldOverrideCatchAllContractsConfig = !_.isUndefined(contractsToCompile) && contractsToCompile.length > 0; - if (shouldOverrideCatchAllContractsConfig) { - compilerOptions.contracts = contractsToCompile; - } - - return compilerOptions; -} - -function _extractStructs(compiledContract: StandardContractOutput): CustomType[] { - let customTypes: CustomType[] = []; - for (const abiDefinition of compiledContract.abi) { - let types: CustomType[] = []; - switch (abiDefinition.type) { - case 'constructor': { - types = _getStructsAsCustomTypes(abiDefinition); - break; - } - case 'function': { - types = _getStructsAsCustomTypes(abiDefinition); - break; - } - case 'event': - case 'fallback': - // No types exist - break; - default: - throw new Error( - `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, - ); - } - customTypes = [...customTypes, ...types]; - } - return customTypes; -} - -function _genDocSection(compiledContract: StandardContractOutput, contractName: string): DocSection { - const docSection: DocSection = { - comment: _.isUndefined(compiledContract.devdoc) ? '' : compiledContract.devdoc.title, - constructors: [], - methods: [], - properties: [], - types: [], - functions: [], - events: [], - }; - - for (const abiDefinition of compiledContract.abi) { - switch (abiDefinition.type) { - case 'constructor': - docSection.constructors.push(_genConstructorDoc(contractName, abiDefinition, compiledContract.devdoc)); - break; - case 'event': - (docSection.events as Event[]).push(_genEventDoc(abiDefinition)); - // note that we're not sending devdoc to _genEventDoc(). - // that's because the type of the events array doesn't have any fields for documentation! - break; - case 'function': - docSection.methods.push(_genMethodDoc(abiDefinition, compiledContract.devdoc)); - break; - case 'fallback': - docSection.methods.push(_genFallbackDoc(abiDefinition, compiledContract.devdoc)); - break; - default: - throw new Error( - `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, - ); - } - } - - return docSection; -} - -function _genConstructorDoc( - contractName: string, - abiDefinition: ConstructorAbi, - devdocIfExists: DevdocOutput | undefined, -): SolidityMethod { - const { parameters, methodSignature } = _genMethodParamsDoc('', abiDefinition.inputs, devdocIfExists); - - const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); - - const constructorDoc: SolidityMethod = { - isConstructor: true, - name: contractName, - callPath: '', - parameters, - returnType: { name: contractName, typeDocType: TypeDocTypes.Reference }, // sad we have to specify this - isConstant: false, - isPayable: abiDefinition.payable, - comment, - }; - - return constructorDoc; -} - -function _devdocMethodDetailsIfExist( - methodSignature: string, - devdocIfExists: DevdocOutput | undefined, -): string | undefined { - let details; - if (!_.isUndefined(devdocIfExists)) { - const devdocMethodsIfExist = devdocIfExists.methods; - if (!_.isUndefined(devdocMethodsIfExist)) { - const devdocMethodIfExists = devdocMethodsIfExist[methodSignature]; - if (!_.isUndefined(devdocMethodIfExists)) { - const devdocMethodDetailsIfExist = devdocMethodIfExists.details; - if (!_.isUndefined(devdocMethodDetailsIfExist)) { - details = devdocMethodDetailsIfExist; - } - } - } - } - return details; -} - -function _genFallbackDoc(abiDefinition: FallbackAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { - const methodSignature = `()`; - const comment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); - - const returnComment = - _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) - ? undefined - : devdocIfExists.methods[methodSignature].return; - - const methodDoc: SolidityMethod = { - isConstructor: false, - name: 'fallback', - callPath: '', - parameters: [], - returnType: { name: 'void', typeDocType: TypeDocTypes.Intrinsic }, - returnComment, - isConstant: true, - isPayable: abiDefinition.payable, - isFallback: true, - comment: _.isEmpty(comment) - ? 'The default fallback function. It is executed on a call to the contract if none of the other functions match the given function identifier (or if no data was supplied at all).' - : comment, - }; - return methodDoc; -} - -function _genMethodDoc(abiDefinition: MethodAbi, devdocIfExists: DevdocOutput | undefined): SolidityMethod { - const name = abiDefinition.name; - const { parameters, methodSignature } = _genMethodParamsDoc(name, abiDefinition.inputs, devdocIfExists); - const devDocComment = _devdocMethodDetailsIfExist(methodSignature, devdocIfExists); - const returnType = _genMethodReturnTypeDoc(abiDefinition.outputs); - const returnComment = - _.isUndefined(devdocIfExists) || _.isUndefined(devdocIfExists.methods[methodSignature]) - ? undefined - : devdocIfExists.methods[methodSignature].return; - - const hasNoNamedParameters = _.isUndefined(_.find(parameters, p => !_.isEmpty(p.name))); - const isGeneratedGetter = hasNoNamedParameters; - const comment = - _.isEmpty(devDocComment) && isGeneratedGetter - ? `This is an auto-generated accessor method of the '${name}' contract instance variable.` - : devDocComment; - const methodDoc: SolidityMethod = { - isConstructor: false, - name, - callPath: '', - parameters, - returnType, - returnComment, - isConstant: abiDefinition.constant, - isPayable: abiDefinition.payable, - comment, - }; - return methodDoc; -} - -function _genEventDoc(abiDefinition: EventAbi): Event { - const eventDoc: Event = { - name: abiDefinition.name, - eventArgs: _genEventArgsDoc(abiDefinition.inputs, undefined), - }; - return eventDoc; -} - -function _genEventArgsDoc(args: EventParameter[], devdocIfExists: DevdocOutput | undefined): EventArg[] { - const eventArgsDoc: EventArg[] = []; - - for (const arg of args) { - const name = arg.name; - - const type: Type = { - name: arg.type, - typeDocType: TypeDocTypes.Intrinsic, - }; - - const eventArgDoc: EventArg = { - isIndexed: arg.indexed, - name, - type, - }; - - eventArgsDoc.push(eventArgDoc); - } - return eventArgsDoc; -} - -/** - * Extract documentation for each method parameter from @param params. - */ -function _genMethodParamsDoc( - name: string, - abiParams: DataItem[], - devdocIfExists: DevdocOutput | undefined, -): { parameters: Parameter[]; methodSignature: string } { - const parameters: Parameter[] = []; - for (const abiParam of abiParams) { - const type = _getTypeFromDataItem(abiParam); - - const parameter: Parameter = { - name: abiParam.name, - comment: '', - isOptional: false, // Unsupported in Solidity, until resolution of https://github.com/ethereum/solidity/issues/232 - type, - }; - parameters.push(parameter); - } - - const methodSignature = `${name}(${abiParams - .map(abiParam => { - if (!_.startsWith(abiParam.type, 'tuple')) { - return abiParam.type; - } else { - // Need to expand tuples: - // E.g: fillOrder(tuple,uint256,bytes) -> fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes) - const isArray = _.endsWith(abiParam.type, '[]'); - const expandedTypes = _.map(abiParam.components, c => c.type); - const type = `(${expandedTypes.join(',')})${isArray ? '[]' : ''}`; - return type; - } - }) - .join(',')})`; - - if (!_.isUndefined(devdocIfExists)) { - const devdocMethodIfExists = devdocIfExists.methods[methodSignature]; - if (!_.isUndefined(devdocMethodIfExists)) { - const devdocParamsIfExist = devdocMethodIfExists.params; - if (!_.isUndefined(devdocParamsIfExist)) { - for (const parameter of parameters) { - parameter.comment = devdocParamsIfExist[parameter.name]; - } - } - } - } - - return { parameters, methodSignature }; -} - -function _genMethodReturnTypeDoc(outputs: DataItem[]): Type { - if (outputs.length > 1) { - const type: Type = { - name: '', - typeDocType: TypeDocTypes.Tuple, - tupleElements: [], - }; - for (const output of outputs) { - const tupleType = _getTypeFromDataItem(output); - (type.tupleElements as Type[]).push(tupleType); - } - return type; - } else if (outputs.length === 1) { - const output = outputs[0]; - const type = _getTypeFromDataItem(output); - return type; - } else { - const type: Type = { - name: 'void', - typeDocType: TypeDocTypes.Intrinsic, - }; - return type; - } -} - -function _capitalize(text: string): string { - return `${text.charAt(0).toUpperCase()}${text.slice(1)}`; -} - -function _dedupStructs(customTypes: CustomType[]): CustomType[] { - const uniqueCustomTypes: CustomType[] = []; - const seenTypes: { [hash: string]: boolean } = {}; - _.each(customTypes, customType => { - const hash = _generateCustomTypeHash(customType); - if (!seenTypes[hash]) { - uniqueCustomTypes.push(customType); - seenTypes[hash] = true; - } - }); - return uniqueCustomTypes; -} - -function _overwriteStructNames(customTypes: CustomType[]): CustomType[] { - const localCustomTypes = _.cloneDeep(customTypes); - _.each(localCustomTypes, customType => { - const hash = _generateCustomTypeHash(customType); - if (!_.isUndefined(customTypeHashToName[hash])) { - customType.name = customTypeHashToName[hash]; - } - }); - return localCustomTypes; -} - -function _generateCustomTypeHash(customType: CustomType): string { - const customTypeWithoutName = _.cloneDeep(customType); - delete customTypeWithoutName.name; - const customTypeWithoutNameStr = JSON.stringify(customTypeWithoutName); - const hash = ethUtil.sha256(customTypeWithoutNameStr).toString('hex'); - return hash; -} - -function _getStructsAsCustomTypes(abiDefinition: AbiDefinition): CustomType[] { - const customTypes: CustomType[] = []; - // We cast to `any` here because we do not know yet if this type of abiDefinition contains - // an `input` key - if (!_.isUndefined((abiDefinition as any).inputs)) { - const methodOrConstructorAbi = abiDefinition as MethodAbi | ConstructorAbi; - _.each(methodOrConstructorAbi.inputs, input => { - if (!_.isUndefined(input.components)) { - const customType = _getCustomTypeFromDataItem(input); - customTypes.push(customType); - } - }); - } - if (!_.isUndefined((abiDefinition as any).outputs)) { - const methodAbi = abiDefinition as MethodAbi; - _.each(methodAbi.outputs, output => { - if (!_.isUndefined(output.components)) { - const customType = _getCustomTypeFromDataItem(output); - customTypes.push(customType); - } - }); - } - return customTypes; -} - -function _getCustomTypeFromDataItem(inputOrOutput: DataItem): CustomType { - const customType: CustomType = { - name: _.capitalize(inputOrOutput.name), - kindString: 'Interface', - children: [], - }; - _.each(inputOrOutput.components, (component: DataItem) => { - const childType = _getTypeFromDataItem(component); - const customTypeChild = { - name: component.name, - type: childType, - }; - // (fabio): Not sure why this type casting is necessary. Seems TS doesn't - // deduce that `customType.children` cannot be undefined anymore after being - // set to `[]` above. - (customType.children as CustomTypeChild[]).push(customTypeChild); - }); - return customType; -} - -function _getNameFromDataItemIfExists(dataItem: DataItem): string | undefined { - if (_.isUndefined(dataItem.components)) { - return undefined; - } - const customType = _getCustomTypeFromDataItem(dataItem); - const hash = _generateCustomTypeHash(customType); - if (_.isUndefined(customTypeHashToName[hash])) { - return undefined; - } - return customTypeHashToName[hash]; -} - -function _getTypeFromDataItem(dataItem: DataItem): Type { - const typeDocType = !_.isUndefined(dataItem.components) ? TypeDocTypes.Reference : TypeDocTypes.Intrinsic; - let typeName: string; - if (typeDocType === TypeDocTypes.Reference) { - const nameIfExists = _getNameFromDataItemIfExists(dataItem); - typeName = _.isUndefined(nameIfExists) ? _capitalize(dataItem.name) : nameIfExists; - } else { - typeName = dataItem.type; - } - - const isArrayType = _.endsWith(dataItem.type, '[]'); - let type: Type; - if (isArrayType) { - // tslint:disable-next-line:custom-no-magic-numbers - typeName = typeDocType === TypeDocTypes.Intrinsic ? typeName.slice(0, -2) : typeName; - type = { - elementType: { name: typeName, typeDocType }, - typeDocType: TypeDocTypes.Array, - name: '', - }; - } else { - type = { name: typeName, typeDocType }; - } - return type; -} -// tslint:disable:max-file-line-count -- cgit From 033340e304a92e4022cd3bce06f612e904a73faf Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 28 Sep 2018 18:38:10 +0100 Subject: Fix typo --- packages/sol-doc/src/cli.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts index 04956510e..11e638785 100644 --- a/packages/sol-doc/src/cli.ts +++ b/packages/sol-doc/src/cli.ts @@ -20,14 +20,14 @@ const JSON_TAB_WIDTH = 4; .demandOption('contracts-dir') .array('contracts') .help().argv; - // Unforunately, the only way to currently retrieve the declared structs within Solidity contracts + // Unfortunately, the only way to currently retrieve the declared structs within Solidity contracts // is to tease them out of the params/return values included in the ABI. These structures do // not include the structs actual name, so we need a mapping to assign the proper name to a // struct. If the name is not in this mapping, the structs name will default to the param/return value // name (which mostly coincide). const customTypeHashToName: { [hash: string]: string } = { '52d4a768701076c7bac06e386e430883975eb398732eccba797fd09dd064a60e': 'Order', - '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResult', + '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResults', c22239cf0d29df1e6cf1be54f21692a8c0b3a48b9367540d4ffff4608b331ce9: 'OrderInfo', c21e9ff31a30941c22e1cb43752114bb467c34dea58947f98966c9030fc8e4a9: 'TraderInfo', '07c2bddc165e0b5005e6244dd4a9771fa61c78c4f42abd687d57567b0768136c': 'MatchedFillResults', -- cgit From dbbf04bc7d6072055911572b41afd2bf95e65c70 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 1 Oct 2018 17:53:19 +0100 Subject: Fix bug in sol-doc --- packages/sol-doc/src/cli.ts | 2 +- packages/sol-doc/src/sol_doc.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/cli.ts b/packages/sol-doc/src/cli.ts index 11e638785..a1847e868 100644 --- a/packages/sol-doc/src/cli.ts +++ b/packages/sol-doc/src/cli.ts @@ -30,7 +30,7 @@ const JSON_TAB_WIDTH = 4; '46f7e8c4d144d11a72ce5338458ea37b933500d7a65e740cbca6d16e350eaa48': 'FillResults', c22239cf0d29df1e6cf1be54f21692a8c0b3a48b9367540d4ffff4608b331ce9: 'OrderInfo', c21e9ff31a30941c22e1cb43752114bb467c34dea58947f98966c9030fc8e4a9: 'TraderInfo', - '07c2bddc165e0b5005e6244dd4a9771fa61c78c4f42abd687d57567b0768136c': 'MatchedFillResults', + '6de3264a1040e027d4bdd29c71e963028238ac4ef060541078a7aced44a4d46f': 'MatchedFillResults', }; const solDoc = new SolDoc(); const doc = await solDoc.generateSolDocAsync(argv.contractsDir, argv.contracts, customTypeHashToName); diff --git a/packages/sol-doc/src/sol_doc.ts b/packages/sol-doc/src/sol_doc.ts index 4b7cbe77c..eda767054 100644 --- a/packages/sol-doc/src/sol_doc.ts +++ b/packages/sol-doc/src/sol_doc.ts @@ -270,15 +270,15 @@ export class SolDoc { } return type; } - private _overwriteStructNames(customTypes: CustomType[], customTypeHashToName?: ObjectMap): CustomType[] { - if (_.isUndefined(customTypeHashToName)) { + private _overwriteStructNames(customTypes: CustomType[]): CustomType[] { + if (_.isUndefined(this._customTypeHashToName)) { return customTypes; } const localCustomTypes = _.cloneDeep(customTypes); - _.each(localCustomTypes, customType => { + _.each(localCustomTypes, (customType, i) => { const hash = SolDoc._generateCustomTypeHash(customType); if (!_.isUndefined(this._customTypeHashToName) && !_.isUndefined(this._customTypeHashToName[hash])) { - customType.name = this._customTypeHashToName[hash]; + localCustomTypes[i].name = this._customTypeHashToName[hash]; } }); return localCustomTypes; -- cgit From 10f3ee32a4bfd2dbb28e24c35006b3e80e069c96 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Tue, 2 Oct 2018 11:07:46 +0200 Subject: Ignore linter issues --- packages/sol-doc/src/sol_doc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/sol-doc/src') diff --git a/packages/sol-doc/src/sol_doc.ts b/packages/sol-doc/src/sol_doc.ts index eda767054..138882c92 100644 --- a/packages/sol-doc/src/sol_doc.ts +++ b/packages/sol-doc/src/sol_doc.ts @@ -302,7 +302,7 @@ export class SolDoc { break; default: throw new Error( - `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, + `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, // tslint:disable-line:no-unnecessary-type-assertion ); } customTypes = [...customTypes, ...types]; @@ -340,7 +340,7 @@ export class SolDoc { break; default: throw new Error( - `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, + `unknown and unsupported AbiDefinition type '${(abiDefinition as AbiDefinition).type}'`, // tslint:disable-line:no-unnecessary-type-assertion ); } } @@ -486,7 +486,7 @@ export class SolDoc { }); } if (!_.isUndefined((abiDefinition as any).outputs)) { - const methodAbi = abiDefinition as MethodAbi; + const methodAbi = abiDefinition as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion _.each(methodAbi.outputs, output => { if (!_.isUndefined(output.components)) { const customType = this._getCustomTypeFromDataItem(output); -- cgit