Giter Club home page Giter Club logo

primitive-dodoc's Introduction

Dodoc

version npm license

Zero-config Hardhat plugin to generate documentation for all your Solidity contracts.

  • 🤪 Zero-configuration required
  • ✅ Compatible with latest Solidity versions (>= 0.8.0)
  • 🔍 Supports events, errors and external / public functions
  • 📖 Default output to Markdown
  • 🔧 Extendable using custom templates

Want to see a live example? Check out Primitive documentation!

📦 Installation

First thing to do is to install the plugin in your Hardhat project:

# Using yarn
yarn add @primitivefi/hardhat-dodoc

# Or using npm
npm i @primitivefi/hardhat-dodoc

Next step is simply to include the plugin into your hardhat.config.js or hardhat.config.ts file:

// Using JavaScript
require('@primitivefi/hardhat-dodoc');

// Using ES6 or TypeScript
import '@primitivefi/hardhat-dodoc';

And you're done! Documentation will be automatically generated on the next compilation and saved into the docs folder at the root of your project.

📝 Usage

The only requirement to use Dodoc is to comment your Solidity contracts using NatSpec format. For example, given the following function:

/// @notice Does another thing when the function is called.
/// @dev More info about doing another thing when the function is called.
/// @param num A random number
/// @return A random variable
function anotherThing(uint256 num) external pure returns (uint256);

Dodoc will take care of everything and will generate the following output:

Methods

anotherThing

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
num uint256 A random number

Returns

Name Type Description
_0 uint256 A random variable

Dodoc is compatible with all the NatSpec tags (except custom ones for now), and can generate documentation for events, custom errors and external / public functions.

By default Dodoc generates new documentation after each compilation, but you can also trigger the task with the following command:

# Using yarn
yarn hardhat dodoc

# Or using npx
npx hardhat dodoc

🔧 Config

Dodoc comes with a default configuration but you can still tweak some parameters. To do it, change your Hardhat config file like this:

import { HardhatUserConfig } from 'hardhat/config';
import '@nomiclabs/hardhat-waffle';
import '@nomiclabs/hardhat-ethers';
import '@primitivefi/hardhat-dodoc';

const config: HardhatUserConfig = {
  // Your Hardhat config...
  dodoc: {
    runOnCompile: true,
    debugMode: true,
    // More options...
  },
};

export default config;

Here are all the configuration parameters that are currently available, but as said above, all of them are entirely optional:

Parameter Description Default value
runOnCompile True if the plugin should generate the documentation on every compilation true
include List of all the contract / interface / library / folder names to include in the documentation generation. An empty array will generate documentation for everything []
exclude List of all the contract / interface / library / folder names to exclude from the documentation generation []
outputDir Output directory of the documentation docs
templatePath Path to the documentation template ./template.sqrl
debugMode Test mode generating additional JSON files used for debugging false
keepFileStructure True if you want to preserve your contracts file structure in the output directory true
freshOutput True if you want to clean the output directory before generating new documentation true

💅 Customize

Dodoc integrates a super cool template engine called SquirrellyJS, allowing anyone to create new output formats easily.

You can checkout the default Markdown template to get some inspiration, as well as SquirrellyJS documentation to learn more about it. Feel free to be creative, any kind of output such as Markdown, MDX, HTML or even JSON is supported!

Once you're satisfied, simply refer to your template using the templatePath parameter in your configuration and Dodoc will use it to output the documentation!

⛑ Help

Feel free to open an issue if you need help or if you encounter a problem! Here are some already known problems though:

  • Due to the technical limitations of the Solidity compiler, the documentation of private and internal functions is not rendered. Hence, the documentation of libraries might be close to empty!
  • Functions that are not commented at all might not be rendered.
  • State variables overriding functions defined by an interface might "erase" the name of the parameters. A current workaround is to name the function parameters using the _0, _1, ... format.
  • Special functions such as constructor, fallback and receive might not be rendered.
  • Custom NatSpec tags @custom:... are not rendered (yet).

primitive-dodoc's People

Contributors

clemlak avatar indeavr avatar jonahsnider avatar julien-devatom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

primitive-dodoc's Issues

Abstract contracts not rendered

If you have an abstract contract in your project, there is no documentation generated for any of it other than the top-level comment block for the contract (ex the title)

The readme of this project does say that

Due to the technical limitations of the Solidity compiler, the documentation of private and internal functions is not rendered. Hence, the documentation of libraries might be close to empty!

I'm not sure if abstract contracts fall under this scope as well, but abstract contract docgen does work in https://github.com/OpenZeppelin/solidity-docgen at least

Inherited event params not rendering properly

getting undefined for the params on inherited events. Currently working around the issue by copy/pasting the events from the inherited interface over to the main contract docs, but wanted to log the issue here for posterity

Function signatures containing structs appear as `undefined`

I noticed that structs do not render in the docs with a description. This can be seen in the docs for set() in Bar.sol found in this repo.

### set

```solidity
function set(IBar.T t) external nonpayable

Parameters

Name Type Description
t IBar.T undefined

Handle Solidity NatSpec whitespace

For a function specification like this:

/// @notice Perform initial contract setup
/// @dev    The initializer modifier ensures this is only called once, the owner should confirm this was properly
///         performed before publishing this contract address.
/// @param  _initialFee          fee to be paid on each sale, in basis points
/// @param  _initialFeeRecipient wallet to collets fees
/// @param  _initialPaymentToken address of the token that is used for settlement

I am hoping it can be parsed as:

Notice:

Perform initial contract setup

Dev:

The initializer modifier ensures this is only called once, the owner should confirm this was properly performed before publishing this contract address.

Param _initialFee:

fee to be paid on each sale, in basis points

Param _initialFeeRecipient :

wallet to collets fees

Param _initialPaymentToken :

address of the token that is used for settlement


Please let me know how this works and how I can help.

I am also the maintainer of the Solidity Style Guide and the Solidity NatSpec specification.

Parameter `runOnCompile` ignored

First of all thanks for the useful package.
I'd like to point out that the parameter runOnCompile set to false does not make any difference and docs are generated as usual when running yarn compile.

Contract/Interface with the same name as another in a different file or directory gets clobbered

The way this is written right now, the docs are written into a single, flat, directory.

This means that any second Contract or Interface with the same name as another - although with a different qualified name, eg in a different file or dir - will overwrite the doc output of the first one.

I suggest that you have a config option to preserve the directory/filename structure of the contracts.

As this repo is closed, I have coded up a suggested way to do this here, on a Pull Request on my fork of your repo:

#27

Allow escaping *see* notation

Currently, if your contract inherits another contract, documentation is generated using a see notation as follows

*See {IERC721-approve}.*

However, this is problematic because {} are reserved characters in MDX2 to indicate running inline Javascript, causing a Could not parse expression with acorn error (see here)

There are two options to fix this:

Option 1) Stop using {}

Either as a feature flag or in general, a different notation other than {} could be used for this notation (I'm not sure if there is any reason this notation was picked in the first place)

Option 2) Add front matter for the generated files to avoid MDX

Docusaurus uses MDX by default, but as of this year it can also be forced to use standard markdown (see here). However, it does seem like right now this setting still doesn't fix our issue (as per facebook/docusaurus#4288 (comment))

Gitbook not rendering @dev properly

When using gitbook with dodoc, it seems the @dev is rendered improperly. For example:

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

creates:

:::note Details Returns the address of the current owner.
:::

Tried with /// instead of /* */ and still no dice.

image

Also using the same docusaurus.sql as Primitive:

---
description: {{@if (it.title)}}{{it.title}}{{/if}}

---

{{@if (it.name)}}# {{it.name}}.sol{{/if}}


{{@if (it.notice)}}{{it.notice}}{{/if}}


{{@if (it.details)}}
:::note Details
{{it.details}}

:::
{{/if}}


{{@if (Object.keys(it.methods).length > 0)}}
## Methods

{{@foreach(it.methods) => key, val}}
### {{key}}


{{@if (val.notice)}}{{val.notice}}{{/if}}

solidity title="Solidity"
{{val.code}}



{{@if (val.details)}}
:::note Details
{{val.details}}

:::
{{/if}}


{{@if (Object.keys(val.inputs).length > 0)}}
#### Parameters

| Name | Type | Description |
|---|---|---|
{{@foreach(val.inputs) => key, val}}
| {{key}} | {{val.type}} | {{val.description}}

{{/foreach}}
{{/if}}

{{@if (Object.keys(val.outputs).length > 0)}}
#### Returns

| Name | Type | Description |
|---|---|---|
{{@foreach(val.outputs) => key, val}}
| {{key}} | {{val.type}} | {{val.description}}

{{/foreach}}

{{/if}}
{{/foreach}}

{{/if}}

{{@if (Object.keys(it.events).length > 0)}}
## Events

{{@foreach(it.events) => key, val}}
### {{key}}


{{@if (val.notice)}}{{val.notice}}{{/if}}


solidity title="Solidity"
{{val.code}}

{{@if (val.details)}}
:::note Details
{{val.details}}

:::
{{/if}}


{{@if (Object.keys(val.inputs).length > 0)}}
#### Parameters

| Name | Type | Description |
|---|---|---|
{{@foreach(val.inputs) => key, val}}
| {{key}} {{@if (val.indexed)}}`indexed`{{/if}} | {{val.type}} | {{val.description}} |
{{/foreach}}
{{/if}}

{{/foreach}}

{{/if}}

{{@if (Object.keys(it.errors).length > 0)}}
## Errors

{{@foreach(it.errors) => key, val}}
### {{key}}


{{@if (val.notice)}}{{val.notice}}{{/if}}


solidity title="Solidity"
{{val.code}}


{{@if (val.details)}}
:::note Details
{{val.details}}

:::
{{/if}}


{{@if (Object.keys(val.inputs).length > 0)}}
#### Parameters

| Name | Type | Description |
|---|---|---|
{{@foreach(val.inputs) => key, val}}
| {{key}} | {{val.type}} | {{val.description}} |
{{/foreach}}
{{/if}}

{{/foreach}}

{{/if}}

Any idea how to work around this?

Include the relative path of the contracts in the debug output

The debug output only stores the information found in the devdoc, userdoc and ABI of the contracts.
However including the relative path of the contracts could be a great addition that would allow custom templates to display a direct URL to the contract for example.

Events which are inherited display `undefined` in the description

If an event is inherited it will not display anything in the description. After digging through this a bit I found that the devdoc and userdoc contain different sets of information if the event was inherited compared to if it was defined on the contract or interface directly. This was not obvious at first but it became very clear after logging the output of the info object for Bar.sol and IBar.sol.

const buildInfo = await hre.artifacts.getBuildInfo(qualifiedName);
const info = buildInfo?.output.contracts[source][name] as CompilerOutputContractWithDocumentation;

const dd: any = buildInfo?.output.contracts[source][name]

console.log(name)
console.log("devdoc event", dd.devdoc.events)
console.log("userdoc event", dd.userdoc.events)
Bar
devdoc event undefined
userdoc event { 'Transfer(uint256)': { notice: 'Emitted when transfer' } }

IBar
devdoc event {
  'Transfer(uint256)': {
    details: 'Transfer some stuff',
    params: { foo: 'Amount of stuff' }
  }
}
userdoc event { 'Transfer(uint256)': { notice: 'Emitted when transfer' } }

Given that the issue is with the incoming data perhaps it makes sense to do the following, 1) when the docs have been created, check if more than one event with the same name, description, and params exists 2) combine the matched events such that they all use the most complete set of information.

Lots of `undefined` for function params descriptions

The generated docs are having a lot of undefined for some function parameters function parameters descriptions, while for some function parameters they are are appearing good, even though the Natspec is in the correct format and the same.

When running on debugMode, the dev docs have the correct parameters and descriptions, without any undefined. But they do not persist till the markdown output

I noticed this in the Primitive docs too: https://library.primitive.xyz/technical/smart-contracts/autogenerated-docs/core/PrimitiveEngine#parameters-1

Create a specific task to generate the documentation

Right now the only way to generate the documentation is to compile the contracts, however, if the runOnCompile setting is set to false, the doc can't be generated without changing the configuration manually.
This is a bit annoying so a custom task could be added to solve this issue.

Unnecessary doc generation for similarly named contract

 // Checks if the documentation has to be generated for this contract
    const includesPath = config.include.some((str) => filePath.includes(str));

The above check in index.ts#L40, seems to be causing a slightly unwanted issue.

If there are two contracts that have the same substring in the contract name, docs are being generated for both contracts, even if I've only included one of them in hre.config.dodoc.include

Eg. contract/ContractName.sol, contracts/test/TestContractName.sol

Docs will be generated for both contracts, even if hre.config.dodoc.include = ['ContractName']

Support for @custom tag

While the standard comment tags (@notice, @dev, ..) work great, Solidity also provides support for @custom:<something>.

Although everyone's use-case will be slightly different from custom tag, it would be super helpful to get a how-to on how to add a custom tag support to docusaurus.sqrl, or even provide sample ones for a common custom tags such as for the events emitted from a function.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.