From e33d0fd280c89ce3be60b73c980d5141532c1d6c Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 17 May 2023 18:34:06 +0300 Subject: [PATCH 1/4] feat: add support for custom tags --- examples/contracts/Bar.sol | 3 + examples/docs/Bar.json | 3 +- examples/docs/Bar.md | 14 +- examples/docs/Foo.md | 4 +- examples/docs/IBar.json | 3 +- examples/docs/IBar.md | 12 +- examples/docs/IExampleContract.md | 20 +- examples/docs/IFoo.md | 4 +- examples/docs/subfolder/AnotherContract.md | 4 +- src/dodocTypes.ts | 241 +++++++++++---------- src/index.ts | 27 +++ src/template.sqrl | 21 +- 12 files changed, 225 insertions(+), 131 deletions(-) diff --git a/examples/contracts/Bar.sol b/examples/contracts/Bar.sol index 1c9c8c4..6cc90ba 100644 --- a/examples/contracts/Bar.sol +++ b/examples/contracts/Bar.sol @@ -21,11 +21,13 @@ interface IBar { /// @notice Emitted when transfer /// @dev Transfer some stuff /// @param foo Amount of stuff + /// @custom:danger This event exposes private info event Transfer(uint256 foo); /// @notice Thrown when doh /// @dev Bad doh error /// @param yay A bool + /// @custom:info Additional info error Doh(bool yay); } @@ -38,6 +40,7 @@ contract Bar is IBar { function set(T memory t) external { } /// @notice Cool function bro + /// @custom:requirement Check this requirement function boop(uint256 bar) external { } /// @notice Alt cool function bro diff --git a/examples/docs/Bar.json b/examples/docs/Bar.json index f63e967..7afa5d5 100644 --- a/examples/docs/Bar.json +++ b/examples/docs/Bar.json @@ -74,7 +74,8 @@ } }, "notice": "Thrown when doh", - "details": "Bad doh error" + "details": "Bad doh error", + "custom:info": "Additional info" } }, "path": "", diff --git a/examples/docs/Bar.md b/examples/docs/Bar.md index d5d9c54..b63313d 100644 --- a/examples/docs/Bar.md +++ b/examples/docs/Bar.md @@ -20,6 +20,8 @@ Baaps the yaps + + #### Parameters | Name | Type | Description | @@ -37,6 +39,8 @@ Cool function bro + + #### Parameters | Name | Type | Description | @@ -53,6 +57,8 @@ Alt cool function bro + + #### Parameters | Name | Type | Description | @@ -70,6 +76,8 @@ function set(IBar.T t) external nonpayable + + #### Parameters | Name | Type | Description | @@ -90,6 +98,8 @@ Emitted when transfer + + #### Parameters | Name | Type | Description | @@ -107,9 +117,11 @@ error Doh(bool yay) ``` Thrown when doh - *Bad doh error* + +**Info:** *Additional info* + #### Parameters | Name | Type | Description | diff --git a/examples/docs/Foo.md b/examples/docs/Foo.md index 521495c..97015e6 100644 --- a/examples/docs/Foo.md +++ b/examples/docs/Foo.md @@ -17,9 +17,11 @@ function nonces(address) external view returns (uint256) ``` Returns the nonce of an address - *Nonces much* + + + #### Parameters | Name | Type | Description | diff --git a/examples/docs/IBar.json b/examples/docs/IBar.json index 386b618..bafc5e1 100644 --- a/examples/docs/IBar.json +++ b/examples/docs/IBar.json @@ -45,7 +45,8 @@ } }, "notice": "Thrown when doh", - "details": "Bad doh error" + "details": "Bad doh error", + "custom:info": "Additional info" } }, "path": "", diff --git a/examples/docs/IBar.md b/examples/docs/IBar.md index 5d32d65..6c9c019 100644 --- a/examples/docs/IBar.md +++ b/examples/docs/IBar.md @@ -20,6 +20,8 @@ function boop(uint256 bar) external nonpayable + + #### Parameters | Name | Type | Description | @@ -36,6 +38,8 @@ function set(IBar.T t) external nonpayable + + #### Parameters | Name | Type | Description | @@ -53,9 +57,11 @@ event Transfer(uint256 foo) ``` Emitted when transfer - *Transfer some stuff* + + + #### Parameters | Name | Type | Description | @@ -73,9 +79,11 @@ error Doh(bool yay) ``` Thrown when doh - *Bad doh error* + +**Info:** *Additional info* + #### Parameters | Name | Type | Description | diff --git a/examples/docs/IExampleContract.md b/examples/docs/IExampleContract.md index 8cc1038..46f74bc 100644 --- a/examples/docs/IExampleContract.md +++ b/examples/docs/IExampleContract.md @@ -17,9 +17,11 @@ function anotherThing(uint256 num) external pure returns (uint256) ``` Does another thing when the function is called. - *More info about doing another thing when the function is called.* + + + #### Parameters | Name | Type | Description | @@ -43,6 +45,8 @@ Poorly documented function starting with weird spaces. + + #### Returns | Name | Type | Description | @@ -56,9 +60,11 @@ function doSomething(address a, uint256 b) external nonpayable returns (uint256 ``` Does something when this function is called. - *More info about the doSomething, and this even works when the explanation is on two lines.* + + + #### Parameters | Name | Type | Description | @@ -86,6 +92,8 @@ A bad documented payable function. + + ## Events ### DoSomething @@ -95,9 +103,11 @@ event DoSomething(address indexed a, uint256 b) ``` Emitted when the function doSomething is called. - *More info about the event can be added here.* + + + #### Parameters | Name | Type | Description | @@ -116,9 +126,11 @@ error RandomError(address expected, address actual) ``` Thrown when an error happens. - *More info about the error.* + + + #### Parameters | Name | Type | Description | diff --git a/examples/docs/IFoo.md b/examples/docs/IFoo.md index d5af430..6c3057f 100644 --- a/examples/docs/IFoo.md +++ b/examples/docs/IFoo.md @@ -17,9 +17,11 @@ function nonces(address _0) external view returns (uint256) ``` Returns the nonce of an address - *Nonces much* + + + #### Parameters | Name | Type | Description | diff --git a/examples/docs/subfolder/AnotherContract.md b/examples/docs/subfolder/AnotherContract.md index efe9924..e04ddd6 100644 --- a/examples/docs/subfolder/AnotherContract.md +++ b/examples/docs/subfolder/AnotherContract.md @@ -17,9 +17,11 @@ function tip(uint256 much, address mop) external nonpayable ``` A strange function - *Someone wrote this weird function...* + + + #### Parameters | Name | Type | Description | diff --git a/src/dodocTypes.ts b/src/dodocTypes.ts index 40df0e8..de2e153 100644 --- a/src/dodocTypes.ts +++ b/src/dodocTypes.ts @@ -1,147 +1,156 @@ -import { CompilerOutputContract } from 'hardhat/types'; +import { CompilerOutputContract } from "hardhat/types"; + +export type CustomTag = `custom:${T}`; export interface ErrorDevdocArrayItem { - details?: string; - params?: { - [key: string]: string; - } + details?: string; + params?: { + [key: string]: string; + }; + [key: CustomTag]: string } declare interface ErrorUserdocArrayItem { - notice?: string; + notice?: string; } export interface CompilerOutputContractWithDocumentation extends CompilerOutputContract { - devdoc?: { - author?: string; - details?: string; - title?: string; - errors?: { - [key: string]: ErrorDevdocArrayItem[] - } - events?: { - [key: string]: { - details: string; - params: { - [key: string]: string; - } - } - } - methods?: { - [key: string]: { - details?: string; - params: { - [key: string]: string; - }, - returns: { - [key: string]: string; - } - } - }, - returns?: { - [key: string]: { - details?: string; - params: { - [key: string]: string; - } - } - } - stateVariables?: { - [key: string]: { + devdoc?: { + author?: string; details?: string; - params: { - [key: string]: string; - } - returns: { - [key: string]: string; - } - } - } - }, - userdoc?: { - errors?: { - [key: string]: ErrorUserdocArrayItem[] - }, - events?: { - [key: string]: { - notice: string; - }, - }, - methods?: { - [key: string]: { - notice: string; - }, - }, - notice?: string; - } + title?: string; + errors?: { + [key: string]: ErrorDevdocArrayItem[]; + }; + events?: { + [key: string]: { + details: string; + params: { + [key: string]: string; + }; + [key: CustomTag]: string + }; + }; + methods?: { + [key: string]: { + details?: string; + params: { + [key: string]: string; + }; + returns: { + [key: string]: string; + }; + [key: CustomTag]: string + }; + }; + returns?: { + [key: string]: { + details?: string; + params: { + [key: string]: string; + }; + }; + }; + stateVariables?: { + [key: string]: { + details?: string; + params: { + [key: string]: string; + }; + returns: { + [key: string]: string; + }; + }; + }; + [key: CustomTag]: string + }; + userdoc?: { + errors?: { + [key: string]: ErrorUserdocArrayItem[]; + }; + events?: { + [key: string]: { + notice: string; + }; + }; + methods?: { + [key: string]: { + notice: string; + }; + }; + notice?: string; + }; } export interface AbiElementPut { - internalType: string; - name: string; - type: string; - indexed?: boolean; + internalType: string; + name: string; + type: string; + indexed?: boolean; } export interface AbiElement { - type: 'constructor' | 'function' | 'event' | 'error'; - name: string; - stateMutability?: string; - inputs: AbiElementPut[]; - outputs: AbiElementPut[]; + type: 'constructor' | 'function' | 'event' | 'error'; + name: string; + stateMutability?: string; + inputs: AbiElementPut[]; + outputs: AbiElementPut[]; } export interface Param { - type?: string; - description?: string; - indexed?: boolean; + type?: string; + description?: string; + indexed?: boolean; } export interface Method { - code?: string; - stateMutability?: string; - notice?: string; - details?: string; - inputs: { - [key: string]: Param; - } - outputs: { - [key: string]: Param; - } + code?: string; + stateMutability?: string; + notice?: string; + details?: string; + inputs: { + [key: string]: Param; + }; + outputs: { + [key: string]: Param; + }; + [key: CustomTag]: string } export interface Event { - code?: string; - notice?: string; - details?: string; - inputs: { - [key: string]: Param; - } + code?: string; + notice?: string; + details?: string; + inputs: { + [key: string]: Param; + }; + [key: CustomTag]: string } export interface Error { - code?: string; - description?: string; - details?: string; - inputs: { - [key: string]: Param; - } + code?: string; + notice?: string; + details?: string; + inputs: { + [key: string]: Param; + }; + [key: CustomTag]: string } export interface Doc { - path?: string; - name?: string; - title?: string; - notice?: string; - details?: string; - author?: string; - methods: { - [key: string]: Method; - } - events: { - [key: string]: Event; - } - errors: { - [key: string]: Event; - } + path?: string; + name?: string; + title?: string; + notice?: string; + details?: string; + author?: string; + methods: { + [key: string]: Method; + }; + events: { + [key: string]: Event; + }; + errors: { + [key: string]: Error; + }; } diff --git a/src/index.ts b/src/index.ts index 56c4b08..77f6143 100644 --- a/src/index.ts +++ b/src/index.ts @@ -96,6 +96,15 @@ async function generateDocumentation( for (const param in error?.params) { if (doc.errors[errorName].inputs[param]) doc.errors[errorName].inputs[param].description = error?.params[param]; } + + for (const value in error) { + if (value.startsWith('custom:')) { + const strippedValue = value.substring(7); + if (strippedValue.length > 0) { + doc.errors[errorName][`custom:${strippedValue}`] = error[`custom:${strippedValue}`]; + } + } + } } for (const eventSig in info.devdoc?.events) { @@ -107,6 +116,15 @@ async function generateDocumentation( for (const param in event?.params) { if (doc.events[eventName].inputs[param]) doc.events[eventName].inputs[param].description = event?.params[param]; } + + for (const value in event) { + if (value.startsWith('custom:')) { + const strippedValue = value.substring(6); + if (strippedValue.length > 0) { + doc.events[eventName][`custom:${strippedValue}`] = event[`custom:${strippedValue}`]; + } + } + } } for (const methodSig in info.devdoc?.methods) { @@ -124,6 +142,15 @@ async function generateDocumentation( if (doc.methods[methodSig].outputs[output]) doc.methods[methodSig].outputs[output].description = method?.returns[output]; } } + + for (const value in method) { + if (value.startsWith('custom:')) { + const strippedValue = value.substring(6); + if (strippedValue.length > 0) { + doc.methods[methodSig][`custom:${strippedValue}`] = method[`custom:${strippedValue}`]; + } + } + } } for (const varName in info.devdoc?.stateVariables) { diff --git a/src/template.sqrl b/src/template.sqrl index 0c263db..5e4ce03 100644 --- a/src/template.sqrl +++ b/src/template.sqrl @@ -27,9 +27,14 @@ {{@if (val.notice)}}{{val.notice}}{{/if}} - {{@if (val.details)}}*{{val.details}}*{{/if}} +{{@if (val['custom:requirement'])}}**Requirement:** *{{val['custom:requirement']}}*{{/if}} + +{{@if (val['custom:danger'])}}**Danger:** *{{val['custom:danger']}}*{{/if}} + +{{@if (val['custom:info'])}}**Info:** *{{val['custom:info']}}*{{/if}} + {{@if (Object.keys(val.inputs).length > 0)}} #### Parameters @@ -69,9 +74,14 @@ {{@if (val.notice)}}{{val.notice}}{{/if}} - {{@if (val.details)}}*{{val.details}}*{{/if}} +{{@if (val['custom:requirement'])}}**Requirement:** *{{val['custom:requirement']}}*{{/if}} + +{{@if (val['custom:danger'])}}**Danger:** *{{val['custom:danger']}}*{{/if}} + +{{@if (val['custom:info'])}}**Info:** *{{val['custom:info']}}*{{/if}} + {{@if (Object.keys(val.inputs).length > 0)}} #### Parameters @@ -101,9 +111,14 @@ {{@if (val.notice)}}{{val.notice}}{{/if}} - {{@if (val.details)}}*{{val.details}}*{{/if}} +{{@if (val['custom:requirement'])}}**Requirement:** *{{val['custom:requirement']}}*{{/if}} + +{{@if (val['custom:danger'])}}**Danger:** *{{val['custom:danger']}}*{{/if}} + +{{@if (val['custom:info'])}}**Info:** *{{val['custom:info']}}*{{/if}} + {{@if (Object.keys(val.inputs).length > 0)}} #### Parameters From e0195e1587b06fd3cf20c8f5b7523ea33ad62584 Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 17 May 2023 18:55:16 +0300 Subject: [PATCH 2/4] Fix bug --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 77f6143..b7926d1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -119,7 +119,7 @@ async function generateDocumentation( for (const value in event) { if (value.startsWith('custom:')) { - const strippedValue = value.substring(6); + const strippedValue = value.substring(7); if (strippedValue.length > 0) { doc.events[eventName][`custom:${strippedValue}`] = event[`custom:${strippedValue}`]; } @@ -145,7 +145,7 @@ async function generateDocumentation( for (const value in method) { if (value.startsWith('custom:')) { - const strippedValue = value.substring(6); + const strippedValue = value.substring(7); if (strippedValue.length > 0) { doc.methods[methodSig][`custom:${strippedValue}`] = method[`custom:${strippedValue}`]; } From ea11e7f50d9c11c435e9b0f73afb88ef0eb58f45 Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 17 May 2023 18:55:20 +0300 Subject: [PATCH 3/4] Add edge case --- examples/contracts/Bar.sol | 3 ++- examples/docs/Bar.json | 3 ++- examples/docs/Bar.md | 2 +- examples/docs/IBar.json | 3 ++- examples/docs/IBar.md | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/contracts/Bar.sol b/examples/contracts/Bar.sol index 6cc90ba..391155c 100644 --- a/examples/contracts/Bar.sol +++ b/examples/contracts/Bar.sol @@ -40,7 +40,8 @@ contract Bar is IBar { function set(T memory t) external { } /// @notice Cool function bro - /// @custom:requirement Check this requirement + /// @custom:requirement Check first requirement + /// @custom:requirement Check second requirement function boop(uint256 bar) external { } /// @notice Alt cool function bro diff --git a/examples/docs/Bar.json b/examples/docs/Bar.json index 7afa5d5..186aaf5 100644 --- a/examples/docs/Bar.json +++ b/examples/docs/Bar.json @@ -25,7 +25,8 @@ } }, "outputs": {}, - "notice": "Cool function bro" + "notice": "Cool function bro", + "custom:requirement": "Check first requirementCheck second requirement" }, "boop(uint256,uint256)": { "stateMutability": "nonpayable", diff --git a/examples/docs/Bar.md b/examples/docs/Bar.md index b63313d..7ac1bfd 100644 --- a/examples/docs/Bar.md +++ b/examples/docs/Bar.md @@ -37,7 +37,7 @@ function boop(uint256 bar) external nonpayable Cool function bro - +**Requirement:** *Check first requirementCheck second requirement* diff --git a/examples/docs/IBar.json b/examples/docs/IBar.json index bafc5e1..f73734f 100644 --- a/examples/docs/IBar.json +++ b/examples/docs/IBar.json @@ -32,7 +32,8 @@ } }, "notice": "Emitted when transfer", - "details": "Transfer some stuff" + "details": "Transfer some stuff", + "custom:danger": "This event exposes private info" } }, "errors": { diff --git a/examples/docs/IBar.md b/examples/docs/IBar.md index 6c9c019..5ab5687 100644 --- a/examples/docs/IBar.md +++ b/examples/docs/IBar.md @@ -59,7 +59,7 @@ event Transfer(uint256 foo) Emitted when transfer *Transfer some stuff* - +**Danger:** *This event exposes private info* #### Parameters From c5a4d35c74fb830d00a436a984b49c18e984a687 Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 17 May 2023 19:11:21 +0300 Subject: [PATCH 4/4] Add support for custom tags in Contract docs --- examples/contracts/Bar.sol | 1 + examples/docs/Bar.json | 1 + examples/docs/Bar.md | 2 ++ examples/docs/Foo.md | 2 ++ examples/docs/IBar.md | 2 ++ examples/docs/IExampleContract.md | 2 ++ examples/docs/IFoo.md | 2 ++ examples/docs/subfolder/AnotherContract.md | 2 ++ src/dodocTypes.ts | 3 ++- src/index.ts | 11 ++++++++++- src/template.sqrl | 3 +++ 11 files changed, 29 insertions(+), 2 deletions(-) diff --git a/examples/contracts/Bar.sol b/examples/contracts/Bar.sol index 391155c..a4fd23b 100644 --- a/examples/contracts/Bar.sol +++ b/examples/contracts/Bar.sol @@ -35,6 +35,7 @@ interface IBar { /// @author Primitive /// @notice Manages the bar /// @dev Blablou +/// @custom:version v2.0.1 contract Bar is IBar { /// @inheritdoc IBar function set(T memory t) external { } diff --git a/examples/docs/Bar.json b/examples/docs/Bar.json index 186aaf5..b491085 100644 --- a/examples/docs/Bar.json +++ b/examples/docs/Bar.json @@ -84,5 +84,6 @@ "notice": "Manages the bar", "details": "Blablou", "author": "Primitive", + "custom:version": "v2.0.1", "name": "Bar" } \ No newline at end of file diff --git a/examples/docs/Bar.md b/examples/docs/Bar.md index 7ac1bfd..53fe173 100644 --- a/examples/docs/Bar.md +++ b/examples/docs/Bar.md @@ -8,6 +8,8 @@ Manages the bar *Blablou* +**Version:** *v2.0.1* + ## Methods ### baap diff --git a/examples/docs/Foo.md b/examples/docs/Foo.md index 97015e6..bf20b76 100644 --- a/examples/docs/Foo.md +++ b/examples/docs/Foo.md @@ -8,6 +8,8 @@ + + ## Methods ### nonces diff --git a/examples/docs/IBar.md b/examples/docs/IBar.md index 5ab5687..8c57f96 100644 --- a/examples/docs/IBar.md +++ b/examples/docs/IBar.md @@ -8,6 +8,8 @@ + + ## Methods ### boop diff --git a/examples/docs/IExampleContract.md b/examples/docs/IExampleContract.md index 46f74bc..2a35fee 100644 --- a/examples/docs/IExampleContract.md +++ b/examples/docs/IExampleContract.md @@ -8,6 +8,8 @@ Put a simple description of the contract here. *And then a more complicated and tech oriented description of the contract there.* + + ## Methods ### anotherThing diff --git a/examples/docs/IFoo.md b/examples/docs/IFoo.md index 6c3057f..cf80dd4 100644 --- a/examples/docs/IFoo.md +++ b/examples/docs/IFoo.md @@ -8,6 +8,8 @@ + + ## Methods ### nonces diff --git a/examples/docs/subfolder/AnotherContract.md b/examples/docs/subfolder/AnotherContract.md index e04ddd6..498d8a8 100644 --- a/examples/docs/subfolder/AnotherContract.md +++ b/examples/docs/subfolder/AnotherContract.md @@ -8,6 +8,8 @@ + + ## Methods ### tip diff --git a/src/dodocTypes.ts b/src/dodocTypes.ts index de2e153..e8d91fc 100644 --- a/src/dodocTypes.ts +++ b/src/dodocTypes.ts @@ -62,7 +62,7 @@ export interface CompilerOutputContractWithDocumentation extends CompilerOutputC }; }; }; - [key: CustomTag]: string + [key: CustomTag]: string; }; userdoc?: { errors?: { @@ -144,6 +144,7 @@ export interface Doc { notice?: string; details?: string; author?: string; + [key: CustomTag]: string; methods: { [key: string]: Method; }; diff --git a/src/index.ts b/src/index.ts index b7926d1..2ee2af3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -62,7 +62,7 @@ async function generateDocumentation( console.log(JSON.stringify(info.devdoc, null, 4)); } - const doc = { ...decodeAbi(info.abi), path: source.substr(sourcesPath.length).split('/').slice(0, -1).join('/') }; // get file path without filename + const doc: Doc = { ...decodeAbi(info.abi), path: source.substr(sourcesPath.length).split('/').slice(0, -1).join('/') }; // get file path without filename // Fetches info from userdoc for (const errorSig in info.userdoc?.errors) { @@ -178,6 +178,15 @@ async function generateDocumentation( if (info.devdoc?.details) doc.details = info.devdoc.details; if (info.devdoc?.author) doc.author = info.devdoc.author; + for (const value in info.devdoc) { + if (value.startsWith('custom:')) { + const strippedValue = value.substring(7); + if (strippedValue.length > 0) { + doc[`custom:${strippedValue}`] = info.devdoc[`custom:${strippedValue}`]; + } + } + } + doc.name = name; docs.push(doc); } diff --git a/src/template.sqrl b/src/template.sqrl index 5e4ce03..15e3a20 100644 --- a/src/template.sqrl +++ b/src/template.sqrl @@ -13,6 +13,9 @@ {{@if (it.details)}}*{{it.details}}*{{/if}} +{{@if (it['custom:version'])}}**Version:** *{{it['custom:version']}}*{{/if}} + + {{@if (Object.keys(it.methods).length > 0)}} ## Methods