瀏覽代碼

Update docs pipeline to solidity-docgen@0.6 (#3707)

(cherry picked from commit c52625018b2afb7bd015cf7f3be20753123b437b)
Francisco 3 年之前
父節點
當前提交
50501a751e

+ 1 - 1
contracts/governance/utils/Votes.sol

@@ -122,7 +122,7 @@ abstract contract Votes is IVotes, Context, EIP712 {
     /**
      * @dev Delegate all of `account`'s voting units to `delegatee`.
      *
-     * Emits events {DelegateChanged} and {DelegateVotesChanged}.
+     * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
      */
     function _delegate(address account, address delegatee) internal virtual {
         address oldDelegate = delegates(account);

+ 1 - 1
contracts/token/ERC20/extensions/ERC20FlashMint.sol

@@ -73,7 +73,7 @@ abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
      * amount + fee tokens and have them approved back to the token contract itself so
      * they can be burned.
      * @param receiver The receiver of the flash loan. Should implement the
-     * {IERC3156FlashBorrower.onFlashLoan} interface.
+     * {IERC3156FlashBorrower-onFlashLoan} interface.
      * @param token The token to be flash loaned. Only `address(this)` is
      * supported.
      * @param amount The amount of tokens to be loaned.

+ 2 - 2
contracts/token/ERC20/extensions/ERC20Votes.sol

@@ -191,7 +191,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit {
     /**
      * @dev Move voting power when tokens are transferred.
      *
-     * Emits a {DelegateVotesChanged} event.
+     * Emits a {IVotes-DelegateVotesChanged} event.
      */
     function _afterTokenTransfer(
         address from,
@@ -206,7 +206,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit {
     /**
      * @dev Change delegation for `delegator` to `delegatee`.
      *
-     * Emits events {DelegateChanged} and {DelegateVotesChanged}.
+     * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
      */
     function _delegate(address delegator, address delegatee) internal virtual {
         address currentDelegate = delegates(delegator);

+ 1 - 1
contracts/token/ERC20/utils/SafeERC20.sol

@@ -104,7 +104,7 @@ library SafeERC20 {
      */
     function _callOptionalReturn(IERC20 token, bytes memory data) private {
         // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
-        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
+        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
         // the target address contains contract code and also asserts for success in the low-level call.
 
         bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");

+ 1 - 1
contracts/token/ERC721/extensions/ERC721Consecutive.sol

@@ -79,7 +79,7 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 {
      *
      * CAUTION: Does not invoke `onERC721Received` on the receiver.
      *
-     * Emits a {ConsecutiveTransfer} event.
+     * Emits a {IERC2309-ConsecutiveTransfer} event.
      */
     function _mintConsecutive(address to, uint96 batchSize) internal virtual returns (uint96) {
         uint96 first = _totalConsecutiveSupply();

+ 2 - 2
contracts/token/ERC721/extensions/ERC721Royalty.sol

@@ -11,8 +11,8 @@ import "../../../utils/introspection/ERC165.sol";
  * @dev Extension of ERC721 with the ERC2981 NFT Royalty Standard, a standardized way to retrieve royalty payment
  * information.
  *
- * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
- * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
+ * Royalty information can be specified globally for all token ids via {ERC2981-_setDefaultRoyalty}, and/or individually for
+ * specific token ids via {ERC2981-_setTokenRoyalty}. The latter takes precedence over the first.
  *
  * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
  * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to

+ 2 - 2
contracts/token/ERC721/extensions/ERC721Votes.sol

@@ -20,7 +20,7 @@ abstract contract ERC721Votes is ERC721, Votes {
     /**
      * @dev Adjusts votes when tokens are transferred.
      *
-     * Emits a {Votes-DelegateVotesChanged} event.
+     * Emits a {IVotes-DelegateVotesChanged} event.
      */
     function _afterTokenTransfer(
         address from,
@@ -34,7 +34,7 @@ abstract contract ERC721Votes is ERC721, Votes {
     /**
      * @dev Adjusts votes when a batch of tokens is transferred.
      *
-     * Emits a {Votes-DelegateVotesChanged} event.
+     * Emits a {IVotes-DelegateVotesChanged} event.
      */
     function _afterConsecutiveTokenTransfer(
         address from,

+ 1 - 1
contracts/token/ERC777/ERC777.sol

@@ -508,7 +508,7 @@ contract ERC777 is Context, IERC777, IERC20 {
      * Does not update the allowance amount in case of infinite allowance.
      * Revert if not enough allowance is available.
      *
-     * Might emit an {Approval} event.
+     * Might emit an {IERC20-Approval} event.
      */
     function _spendAllowance(
         address owner,

+ 2 - 2
contracts/utils/structs/EnumerableMap.sol

@@ -145,10 +145,10 @@ library EnumerableMap {
     }
 
     /**
-     * @dev Same as {_get}, with a custom error message when `key` is not in the map.
+     * @dev Same as {get}, with a custom error message when `key` is not in the map.
      *
      * CAUTION: This function is deprecated because it requires allocating memory for the error
-     * message unnecessarily. For custom revert reasons use {_tryGet}.
+     * message unnecessarily. For custom revert reasons use {tryGet}.
      */
     function get(
         Bytes32ToBytes32Map storage map,

+ 21 - 0
docs/config.js

@@ -0,0 +1,21 @@
+const path = require('path');
+const fs = require('fs');
+
+/** @type import('solidity-docgen/dist/config').UserConfig */
+module.exports = {
+  outputDir: 'docs/modules/api/pages',
+  templates: 'docs/templates',
+  exclude: ['mocks'],
+  pageExtension: '.adoc',
+  pages: (_, file, config) => {
+    // For each contract file, find the closest README.adoc and return its location as the output page path.
+    const sourcesDir = path.resolve(config.root, config.sourcesDir);
+    let dir = path.resolve(config.root, file.absolutePath);
+    while (dir.startsWith(sourcesDir)) {
+      dir = path.dirname(dir);
+      if (fs.existsSync(path.join(dir, 'README.adoc'))) {
+        return path.relative(sourcesDir, dir) + config.pageExtension;
+      }
+    }
+  },
+};

+ 0 - 91
docs/contract.hbs

@@ -1,91 +0,0 @@
-{{~#*inline "typed-variable-array"~}}
-{{#each .}}{{typeName}}{{#if name}} {{name}}{{/if}}{{#unless @last}}, {{/unless}}{{/each}}
-{{~/inline~}}
-
-{{#each linkable}}
-:{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]]
-{{/each}}
-
-[.contract]
-[[{{anchor}}]]
-=== `++{{name}}++` link:{{github-link file.path}}[{github-icon},role=heading-link]
-
-[.hljs-theme-light.nopadding]
-```solidity
-import "@openzeppelin/contracts/{{file.path}}";
-```
-
-{{natspec.devdoc}}
-
-{{#if ownModifiers}}
-[.contract-index]
-.Modifiers
---
-{{#each ownModifiers}}
-* {xref-{{slug anchor~}} }[`++{{name}}({{args.names}})++`]
-{{/each}}
---
-{{/if}}
-
-{{#if functions}}
-[.contract-index]
-.Functions
---
-{{#each inheritedItems}}
-{{#if (or @first (ne contract.name "Context"))}}
-{{#unless @first}}
-[.contract-subindex-inherited]
-.{{contract.name}}
-{{/unless}}
-{{#each functions}}
-* {xref-{{slug anchor~}} }[`++{{name}}({{args.names}})++`]
-{{/each}}
-
-{{/if}}
-{{/each}}
---
-{{/if}}
-
-{{#if events}}
-[.contract-index]
-.Events
---
-{{#each inheritedItems}}
-{{#unless @first}}
-[.contract-subindex-inherited]
-.{{contract.name}}
-{{/unless}}
-{{#each events}}
-* {xref-{{slug anchor~}} }[`++{{name}}({{args.names}})++`]
-{{/each}}
-
-{{/each}}
---
-{{/if}}
-
-{{#each ownModifiers}}
-[.contract-item]
-[[{{anchor}}]]
-==== `[.contract-item-name]#++{{name}}++#++({{> typed-variable-array args}})++` [.item-kind]#modifier#
-
-{{natspec.devdoc}}
-
-{{/each}}
-
-{{#each ownFunctions}}
-[.contract-item]
-[[{{anchor}}]]
-==== `[.contract-item-name]#++{{name}}++#++({{> typed-variable-array args}}){{#if outputs}} → {{> typed-variable-array outputs}}{{/if}}++` [.item-kind]#{{visibility}}#
-
-{{natspec.devdoc}}
-
-{{/each}}
-
-{{#each ownEvents}}
-[.contract-item]
-[[{{anchor}}]]
-==== `[.contract-item-name]#++{{name}}++#++({{> typed-variable-array args}})++` [.item-kind]#event#
-
-{{natspec.devdoc}}
-
-{{/each}}

+ 0 - 10
docs/helpers.js

@@ -1,10 +0,0 @@
-const { version } = require('../package.json');
-
-module.exports = {
-  'github-link': (contractPath) => {
-    if (typeof contractPath !== 'string') {
-      throw new Error('Missing argument');
-    }
-    return `https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v${version}/contracts/${contractPath}`;
-  },
-};

+ 0 - 6
docs/prelude.hbs

@@ -1,6 +0,0 @@
-:github-icon: pass:[<svg class="icon"><use href="#github-icon"/></svg>]
-
-{{#links}}
-:{{slug target.fullName}}: pass:normal[xref:{{path}}#{{target.anchor}}[`{{target.fullName}}`]]
-:xref-{{slug target.anchor}}: xref:{{path}}#{{target.anchor}}
-{{/links}}

+ 85 - 0
docs/templates/contract.hbs

@@ -0,0 +1,85 @@
+{{#each items}}
+:{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]]
+{{/each}}
+
+[.contract]
+[[{{anchor}}]]
+=== `++{{name}}++` link:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v{{oz-version}}/{{__item_context.file.absolutePath}}[{github-icon},role=heading-link]
+
+[.hljs-theme-light.nopadding]
+```solidity
+import "@openzeppelin/{{__item_context.file.absolutePath}}";
+```
+
+{{{natspec.dev}}}
+
+{{#if modifiers}}
+[.contract-index]
+.Modifiers
+--
+{{#each modifiers}}
+* {xref-{{anchor~}} }[`++{{name}}({{names params}})++`]
+{{/each}}
+--
+{{/if}}
+
+{{#if has-functions}}
+[.contract-index]
+.Functions
+--
+{{#each inherited-functions}}
+{{#unless @first}}
+[.contract-subindex-inherited]
+.{{contract.name}}
+{{/unless}}
+{{#each functions}}
+* {xref-{{anchor~}} }[`++{{name}}({{names params}})++`]
+{{/each}}
+
+{{/each}}
+--
+{{/if}}
+
+{{#if has-events}}
+[.contract-index]
+.Events
+--
+{{#each inheritance}}
+{{#unless @first}}
+[.contract-subindex-inherited]
+.{{name}}
+{{/unless}}
+{{#each events}}
+* {xref-{{anchor~}} }[`++{{name}}({{names params}})++`]
+{{/each}}
+
+{{/each}}
+--
+{{/if}}
+
+{{#each modifiers}}
+[.contract-item]
+[[{{anchor}}]]
+==== `[.contract-item-name]#++{{name}}++#++({{typed-params params}})++` [.item-kind]#modifier#
+
+{{{natspec.dev}}}
+
+{{/each}}
+
+{{#each functions}}
+[.contract-item]
+[[{{anchor}}]]
+==== `[.contract-item-name]#++{{name}}++#++({{typed-params params}}){{#if returns}} → {{typed-params returns}}{{/if}}++` [.item-kind]#{{visibility}}#
+
+{{{natspec.dev}}}
+
+{{/each}}
+
+{{#each events}}
+[.contract-item]
+[[{{anchor}}]]
+==== `[.contract-item-name]#++{{name}}++#++({{typed-params params}})++` [.item-kind]#event#
+
+{{{natspec.dev}}}
+
+{{/each}}

+ 46 - 0
docs/templates/helpers.js

@@ -0,0 +1,46 @@
+const { version } = require('../../package.json');
+
+module.exports['oz-version'] = () => version;
+
+module.exports['readme-path'] = (opts) => {
+  return 'contracts/' + opts.data.root.id.replace(/\.adoc$/, '') + '/README.adoc';
+};
+
+module.exports.names = (params) => params.map(p => p.name).join(', ');
+
+module.exports['typed-params'] = (params) => {
+  return params.map(p => `${p.type}${p.name ? ' ' + p.name : ''}`).join(', ');
+};
+
+const slug = module.exports.slug = (str) => {
+  if (str === undefined) {
+    throw new Error('Missing argument');
+  }
+  return str.replace(/\W/g, '-');
+};
+
+const linksCache = new WeakMap();
+
+function getAllLinks (items) {
+  if (linksCache.has(items)) {
+    return linksCache.get(items);
+  }
+  const res = {};
+  linksCache.set(items, res);
+  for (const item of items) {
+    res[`xref-${item.anchor}`] = `xref:${item.__item_context.page}#${item.anchor}`;
+    res[slug(item.fullName)] = `pass:normal[xref:${item.__item_context.page}#${item.anchor}[\`${item.fullName}\`]]`;
+  }
+  return res;
+}
+
+module.exports['with-prelude'] = (opts) => {
+  const links = getAllLinks(opts.data.site.items);
+  const contents = opts.fn();
+  const neededLinks = contents
+    .match(/\{[-._a-z0-9]+\}/ig)
+    .map(m => m.replace(/^\{(.+)\}$/, '$1'))
+    .filter(k => k in links);
+  const prelude = neededLinks.map(k => `:${k}: ${links[k]}`).join('\n');
+  return prelude + '\n' + contents;
+};

+ 4 - 0
docs/templates/page.hbs

@@ -0,0 +1,4 @@
+:github-icon: pass:[<svg class="icon"><use href="#github-icon"/></svg>]
+{{#with-prelude}}
+{{readme (readme-path)}}
+{{/with-prelude}}

+ 49 - 0
docs/templates/properties.js

@@ -0,0 +1,49 @@
+const { isNodeType } = require('solidity-ast/utils');
+const { slug } = require('./helpers');
+
+module.exports.anchor = function anchor ({ item, contract }) {
+  let res = '';
+  if (contract) {
+    res += contract.name + '-';
+  }
+  res += item.name;
+  if ('parameters' in item) {
+    const signature = item.parameters.parameters.map(v => v.typeName.typeDescriptions.typeString).join(',');
+    res += slug('(' + signature + ')');
+  }
+  if (isNodeType('VariableDeclaration', item)) {
+    res += '-' + slug(item.typeName.typeDescriptions.typeString);
+  }
+  return res;
+};
+
+module.exports.inheritance = function ({ item, build }) {
+  if (!isNodeType('ContractDefinition', item)) {
+    throw new Error('used inherited-items on non-contract');
+  }
+
+  return item.linearizedBaseContracts
+    .map(id => build.deref('ContractDefinition', id))
+    .filter((c, i) => c.name !== 'Context' || i === 0);
+};
+
+module.exports['has-functions'] = function ({ item }) {
+  return item.inheritance.some(c => c.functions.length > 0);
+};
+
+module.exports['has-events'] = function ({ item }) {
+  return item.inheritance.some(c => c.events.length > 0);
+};
+
+module.exports['inherited-functions'] = function ({ item }) {
+  const { inheritance } = item;
+  const baseFunctions = new Set(
+    inheritance.flatMap(c => c.functions.flatMap(f => f.baseFunctions ?? [])),
+  );
+  return inheritance.map((contract, i) => ({
+    contract,
+    functions: contract.functions.filter(f =>
+      !baseFunctions.has(f.id) && (f.name !== 'constructor' || i === 0),
+    ),
+  }));
+};

+ 3 - 0
hardhat.config.js

@@ -52,6 +52,8 @@ const argv = require('yargs/yargs')()
 require('@nomiclabs/hardhat-truffle5');
 require('hardhat-ignore-warnings');
 
+require('solidity-docgen');
+
 if (argv.gas) {
   require('hardhat-gas-reporter');
 }
@@ -95,6 +97,7 @@ module.exports = {
     outputFile: argv.gasReport,
     coinmarketcap: argv.coinmarketcap,
   },
+  docgen: require('./docs/config'),
 };
 
 if (argv.coverage) {

文件差異過大導致無法顯示
+ 641 - 604
package-lock.json


+ 5 - 4
package.json

@@ -13,8 +13,8 @@
   "scripts": {
     "compile": "hardhat compile",
     "coverage": "env COVERAGE=true hardhat coverage",
-    "docs": "oz-docs",
-    "docs:watch": "npm run docs watch contracts 'docs/*.hbs' docs/helpers.js",
+    "docs": "npm run prepare-docs && oz-docs",
+    "docs:watch": "oz-docs watch contracts 'docs/templates' docs/config.js",
     "prepare-docs": "scripts/prepare-docs.sh",
     "lint": "npm run lint:js && npm run lint:sol",
     "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix",
@@ -56,7 +56,7 @@
     "@nomicfoundation/hardhat-network-helpers": "^1.0.3",
     "@nomiclabs/hardhat-truffle5": "^2.0.5",
     "@nomiclabs/hardhat-web3": "^2.0.0",
-    "@openzeppelin/docs-utils": "^0.1.0",
+    "@openzeppelin/docs-utils": "^0.1.3",
     "@openzeppelin/test-helpers": "^0.5.13",
     "chai": "^4.2.0",
     "eslint": "^7.32.0",
@@ -68,6 +68,7 @@
     "eth-sig-util": "^3.0.0",
     "ethereumjs-util": "^7.0.7",
     "ethereumjs-wallet": "^1.0.1",
+    "glob": "^8.0.3",
     "graphlib": "^2.1.8",
     "hardhat": "^2.9.1",
     "hardhat-gas-reporter": "^1.0.4",
@@ -84,7 +85,7 @@
     "solhint": "^3.3.6",
     "solidity-ast": "^0.4.25",
     "solidity-coverage": "^0.8.0",
-    "solidity-docgen": "^0.5.3",
+    "solidity-docgen": "^0.6.0-beta.29",
     "web3": "^1.3.0",
     "yargs": "^17.0.0"
   }

+ 2 - 4
scripts/gen-nav.js

@@ -1,14 +1,12 @@
 #!/usr/bin/env node
 
 const path = require('path');
-const proc = require('child_process');
+const glob = require('glob');
 const startCase = require('lodash.startcase');
 
 const baseDir = process.argv[2];
 
-const files = proc.execFileSync(
-  'find', [baseDir, '-type', 'f'], { encoding: 'utf8' },
-).split('\n').filter(s => s !== '');
+const files = glob.sync(baseDir + '/**/*.adoc').map(f => path.relative(baseDir, f));
 
 console.log('.API');
 

+ 2 - 2
scripts/generate/templates/EnumerableMap.js

@@ -154,10 +154,10 @@ function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns
 }
 
 /**
- * @dev Same as {_get}, with a custom error message when \`key\` is not in the map.
+ * @dev Same as {get}, with a custom error message when \`key\` is not in the map.
  *
  * CAUTION: This function is deprecated because it requires allocating memory for the error
- * message unnecessarily. For custom revert reasons use {_tryGet}.
+ * message unnecessarily. For custom revert reasons use {tryGet}.
  */
 function get(
     Bytes32ToBytes32Map storage map,

+ 0 - 16
scripts/prepare-docs-solc.js

@@ -1,16 +0,0 @@
-const hre = require('hardhat');
-
-const { getCompilersDir } = require('hardhat/internal/util/global-dir');
-const { CompilerDownloader } = require('hardhat/internal/solidity/compiler/downloader');
-const { Compiler } = require('hardhat/internal/solidity/compiler');
-
-const [{ version }] = hre.config.solidity.compilers;
-
-async function getSolc () {
-  const downloader = new CompilerDownloader(await getCompilersDir(), { forceSolcJs: true });
-  const { compilerPath } = await downloader.getDownloadedCompilerPath(version);
-  const compiler = new Compiler(compilerPath);
-  return compiler.getSolc();
-}
-
-module.exports = Object.assign(getSolc(), { __esModule: true });

+ 2 - 10
scripts/prepare-docs.sh

@@ -2,7 +2,7 @@
 
 set -o errexit
 
-OUTDIR=docs/modules/api/pages/
+OUTDIR="$(node -p 'require("./docs/config.js").outputDir')"
 
 if [ ! -d node_modules ]; then
   npm ci
@@ -10,14 +10,6 @@ fi
 
 rm -rf "$OUTDIR"
 
-solidity-docgen \
-  -t docs \
-  -o "$OUTDIR" \
-  -e contracts/mocks,contracts/examples \
-  --output-structure readmes \
-  --helpers ./docs/helpers.js \
-  --solc-module ./scripts/prepare-docs-solc.js
-
-rm -f "$OUTDIR"/token/*/presets.md
+hardhat docgen
 
 node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"

部分文件因文件數量過多而無法顯示