solidity-docgen
Version:
Documentation generator for Solidity smart contracts.
117 lines • 5.13 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseNatspec = void 0;
const utils_1 = require("solidity-ast/utils");
const site_1 = require("../site");
const arrays_equal_1 = require("./arrays-equal");
const execall_1 = require("./execall");
const item_type_1 = require("./item-type");
const ItemError_1 = require("./ItemError");
const read_item_docs_1 = require("./read-item-docs");
const scope_1 = require("./scope");
function parseNatspec(item) {
var _a;
if (!item[site_1.DOC_ITEM_CONTEXT])
throw new Error(`Not an item or item is missing context`);
let res = {};
const docSource = (0, read_item_docs_1.readItemDocs)(item);
const docString = docSource !== undefined
? cleanUpDocstringFromSource(docSource)
: 'documentation' in item && item.documentation
? typeof item.documentation === 'string'
? item.documentation
: cleanUpDocstringFromSolc(item.documentation.text)
: '';
const tagMatches = (0, execall_1.execAll)(/^(?:@(\w+|custom:[a-z][a-z-]*) )?((?:(?!^@(?:\w+|custom:[a-z][a-z-]*) )[^])*)/m, docString);
let inheritFrom;
for (const [, tag = 'notice', content] of tagMatches) {
if (content === undefined)
throw new ItemError_1.ItemError('Unexpected error', item);
if (tag === 'dev' || tag === 'notice') {
res[tag] ?? (res[tag] = '');
res[tag] += content;
}
if (tag === 'title') {
res.title = content.trim();
}
if (tag === 'param') {
const paramMatches = content.match(/(\w+) ([^]*)/);
if (paramMatches) {
const [, name, description] = paramMatches;
res.params ?? (res.params = []);
res.params.push({ name, description: description.trim() });
}
}
if (tag === 'return') {
if (!('returnParameters' in item)) {
throw new ItemError_1.ItemError(`Item does not contain return parameters`, item);
}
res.returns ?? (res.returns = []);
const i = res.returns.length;
const p = item.returnParameters.parameters[i];
if (p === undefined) {
throw new ItemError_1.ItemError('Got more @return tags than expected', item);
}
if (!p.name) {
res.returns.push({ description: content.trim() });
}
else {
const paramMatches = content.match(/(\w+)( ([^]*))?/);
if (!paramMatches || paramMatches[1] !== p.name) {
throw new ItemError_1.ItemError(`Expected @return tag to start with name '${p.name}'`, item);
}
const [, name, description] = paramMatches;
res.returns.push({ name, description: description?.trim() ?? '' });
}
}
if (tag?.startsWith('custom:')) {
const key = tag.replace(/^custom:/, '');
res.custom ?? (res.custom = {});
(_a = res.custom)[key] ?? (_a[key] = '');
res.custom[key] += content;
}
if (tag === 'inheritdoc') {
if (!(item.nodeType === 'FunctionDefinition' || item.nodeType === 'VariableDeclaration')) {
throw new ItemError_1.ItemError(`Expected function or variable but saw ${(0, item_type_1.itemType)(item)}`, item);
}
const parentContractName = content.trim();
const parentContract = (0, scope_1.getContractsInScope)(item)[parentContractName];
if (!parentContract) {
throw new ItemError_1.ItemError(`Parent contract '${parentContractName}' not found`, item);
}
inheritFrom = [...(0, utils_1.findAll)('FunctionDefinition', parentContract)].find(f => item.baseFunctions?.includes(f.id));
}
}
if (docString.length === 0) {
if ('baseFunctions' in item && item.baseFunctions?.length === 1) {
const baseFn = item[site_1.DOC_ITEM_CONTEXT].build.deref('FunctionDefinition', item.baseFunctions[0]);
const shouldInherit = item.nodeType === 'VariableDeclaration' || (0, arrays_equal_1.arraysEqual)(item.parameters.parameters, baseFn.parameters.parameters, p => p.name);
if (shouldInherit) {
inheritFrom = baseFn;
}
}
}
if (res.dev)
res.dev = res.dev.trim();
if (res.notice)
res.notice = res.notice.trim();
if (inheritFrom) {
res = { ...parseNatspec(inheritFrom), ...res };
}
return res;
}
exports.parseNatspec = parseNatspec;
// Fix solc buggy parsing of doc comments.
// Reverse engineered from solc behavior.
function cleanUpDocstringFromSolc(text) {
return text
.replace(/\n\n?^[ \t]*(?:\*|\/\/\/)/mg, '\n\n')
.replace(/^[ \t]?/mg, '');
}
function cleanUpDocstringFromSource(text) {
return text
.replace(/^\/\*\*(.*)\*\/$/s, '$1')
.trim()
.replace(/^[ \t]*(\*|\/\/\/)[ \t]?/mg, '');
}
//# sourceMappingURL=natspec.js.map
;