UNPKG

solidity-docgen

Version:

Solidity API documentation automatic generator.

427 lines 14.3 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceContract = exports.Source = void 0; const lodash_1 = require("lodash"); const path_1 = __importDefault(require("path")); const memoize_1 = require("./memoize"); const handlebars_1 = require("./handlebars"); class Source { constructor(contractsDir, solcOutput, contractTemplate) { this.contractsDir = contractsDir; this.solcOutput = solcOutput; this.contractTemplate = contractTemplate; } get contracts() { return lodash_1.flatten(this.files.map(file => file.contracts)); } get files() { return Object.keys(this.solcOutput.sources) .map(fileName => this.file(fileName)); } file(fileName) { return new SourceFile(this, this.solcOutput.sources[fileName].ast, path_1.default.relative(this.contractsDir, fileName)); } fileById(id) { const file = this.files.find(f => f.astId === id); if (file === undefined) { throw new Error(`File with id ${id} not found`); } return file; } contractById(id) { const contract = this.contracts.find(c => c.astId === id); if (contract === undefined) { throw new Error(`Contract with id ${id} not found`); } return contract; } } __decorate([ memoize_1.memoize ], Source.prototype, "file", null); exports.Source = Source; class SourceFile { constructor(source, ast, path) { this.source = source; this.ast = ast; this.path = path; } get contracts() { const astNodes = this.ast.nodes.filter(isContractDefinition); return astNodes.map(node => new SourceContract(this.source, this, node)); } get contractsInScope() { var _a; const scope = {}; for (const c of this.contracts) { scope[c.name] = c; } const imports = this.ast.nodes.filter(isImportDirective); for (const i of imports) { const importedFile = this.source.fileById(i.sourceUnit); if (i.symbolAliases.length === 0) { Object.assign(scope, importedFile.contractsInScope); } else { for (const a of i.symbolAliases) { scope[(_a = a.local) !== null && _a !== void 0 ? _a : a.foreign.name] = importedFile.contractsInScope[a.foreign.name]; } } } ; return scope; } get astId() { return this.ast.id; } } __decorate([ memoize_1.memoize ], SourceFile.prototype, "contracts", null); __decorate([ memoize_1.memoize ], SourceFile.prototype, "contractsInScope", null); class SourceContract { constructor(source, file, astNode) { this.source = source; this.file = file; this.astNode = astNode; } toString() { return this.source.contractTemplate(this); } get name() { return this.astNode.name; } get fullName() { return this.name; } get anchor() { return this.name; } get linkable() { return [this, ...this.ownModifiers, ...this.ownVariables, ...this.ownFunctions, ...this.ownEvents]; } get inheritance() { return this.astNode.linearizedBaseContracts.map(id => this.source.contractById(id)); } get variables() { return lodash_1.flatten(this.inheritance.map(c => c.ownVariables)); } get ownVariables() { return this.astNode.nodes .filter(isVariableDeclaration) .filter(n => n.visibility !== 'private') .map(n => new SourceStateVariable(this, n)); } get functions() { return lodash_1.uniqBy(lodash_1.flatten(this.inheritance.map(c => c.ownFunctions)), f => f.name === 'constructor' ? 'constructor' : f.signature); } get ownFunctions() { return this.astNode.nodes .filter(isFunctionDefinition) .filter(n => n.visibility !== 'private') .map(n => new SourceFunction(this, n)) .filter(f => !f.isTrivialConstructor); } get privateFunctions() { return this.astNode.nodes .filter(isFunctionDefinition) .filter(n => n.visibility === 'private') .map(n => new SourceFunction(this, n)); } get inheritedItems() { const variables = lodash_1.groupBy(this.variables, f => f.contract.astId); const functions = lodash_1.groupBy(this.functions, f => f.contract.astId); const events = lodash_1.groupBy(this.events, f => f.contract.astId); const modifiers = lodash_1.groupBy(this.modifiers, f => f.contract.astId); return this.inheritance.map(contract => ({ contract, variables: variables[contract.astId], functions: functions[contract.astId], events: events[contract.astId], modifiers: modifiers[contract.astId], })); } get events() { return lodash_1.uniqBy(lodash_1.flatten(this.inheritance.map(c => c.ownEvents)), f => f.signature); } get ownEvents() { return this.astNode.nodes .filter(isEventDefinition) .map(n => new SourceEvent(this, n)); } get modifiers() { return lodash_1.uniqBy(lodash_1.flatten(this.inheritance.map(c => c.ownModifiers)), f => f.signature); } get ownModifiers() { return this.astNode.nodes .filter(isModifierDefinition) .map(n => new SourceModifier(this, n)); } get natspec() { if (this.astNode.documentation === null || this.astNode.documentation === undefined) { return {}; } return parseNatSpec(this.astNode.documentation, this); } get astId() { return this.astNode.id; } } __decorate([ memoize_1.memoize ], SourceContract.prototype, "ownVariables", null); __decorate([ memoize_1.memoize ], SourceContract.prototype, "ownFunctions", null); __decorate([ memoize_1.memoize ], SourceContract.prototype, "privateFunctions", null); __decorate([ memoize_1.memoize ], SourceContract.prototype, "ownEvents", null); __decorate([ memoize_1.memoize ], SourceContract.prototype, "ownModifiers", null); __decorate([ memoize_1.memoize ], SourceContract.prototype, "natspec", null); exports.SourceContract = SourceContract; class SourceContractItem { constructor(contract) { this.contract = contract; } get name() { return this.astNode.name; } get fullName() { return `${this.contract.name}.${this.name}`; } get anchor() { return `${this.contract.name}-${handlebars_1.slug(this.signature)}`; } get args() { return SourceTypedVariableArray.fromParameterList(this.astNode.parameters); } get signature() { return `${this.name}(${this.args.map(a => a.type).join(',')})`; } get natspec() { if (this.astNode.documentation === null || this.astNode.documentation === undefined) { return {}; } return parseNatSpec(this.astNode.documentation, this); } } __decorate([ memoize_1.memoize ], SourceContractItem.prototype, "args", null); __decorate([ memoize_1.memoize ], SourceContractItem.prototype, "natspec", null); class SourceStateVariable { constructor(contract, astNode) { this.contract = contract; this.astNode = astNode; } get name() { return this.astNode.name; } get fullName() { return `${this.contract.name}.${this.name}`; } get anchor() { return `${this.contract.name}-${this.name}-${handlebars_1.slug(this.type)}`; } get type() { return this.astNode.typeName.typeDescriptions.typeString; } get signature() { return `${this.type} ${this.name}`; } get natspec() { warnStateVariableNatspec(); return {}; } } class SourceFunction extends SourceContractItem { constructor(contract, astNode) { super(contract); this.astNode = astNode; } get name() { const { name, kind } = this.astNode; const isRegularFunction = kind === 'function'; return isRegularFunction ? name : kind; } get outputs() { return SourceTypedVariableArray.fromParameterList(this.astNode.returnParameters); } get visibility() { return this.astNode.visibility; } get isTrivialConstructor() { return (this.astNode.kind === "constructor" && this.visibility === "public" && this.args.length === 0 && Object.keys(this.natspec).length === 0); } } __decorate([ memoize_1.memoize ], SourceFunction.prototype, "outputs", null); class SourceEvent extends SourceContractItem { constructor(contract, astNode) { super(contract); this.astNode = astNode; } } class SourceModifier extends SourceContractItem { constructor(contract, astNode) { super(contract); this.astNode = astNode; } } class SourceTypedVariable { constructor(typeNode, name) { this.typeNode = typeNode; this.name = name; } get type() { return this.typeNode.typeDescriptions.typeString; } // TODO: deprecate get typeName() { return this.type; } toString() { if (this.name) { return [this.type, this.name].join(' '); } else { return this.type; } } } class PrettyArray extends Array { toString() { return this.map(e => e.toString()).join(', '); } } class SourceTypedVariableArray extends PrettyArray { static fromParameterList(parameters) { return SourceTypedVariableArray.from(parameters.parameters.map(p => new SourceTypedVariable(p.typeName, p.name || undefined))); } get types() { return this.map(v => v.type); } get names() { return this.map(v => (v.name === undefined) ? '_' : v.name); } } function parseNatSpec(doc, context) { var _a, _b, _c, _d, _e, _f; var _g; const res = {}; const tagMatches = execall(/^(?:@(\w+|custom:[a-z][a-z-]*) )?((?:(?!^@(?:\w+|custom:[a-z][a-z-]*) )[^])*)/m, doc); let inheritFrom; for (const [, tag, content] of tagMatches) { if (tag === 'dev') { (_a = res.devdoc) !== null && _a !== void 0 ? _a : (res.devdoc = ''); res.devdoc += content; } if (tag === 'notice' || tag === undefined) { (_b = res.userdoc) !== null && _b !== void 0 ? _b : (res.userdoc = ''); res.userdoc += content; } if (tag === 'title') { res.title = content; } if (tag === 'param') { const paramMatches = content.match(/(\w+) ([^]*)/); if (paramMatches) { const [, param, description] = paramMatches; (_c = res.params) !== null && _c !== void 0 ? _c : (res.params = []); res.params.push({ param, description }); } } if (tag === 'return') { const paramMatches = content.match(/(\w+) ([^]*)/); if (paramMatches) { const [, param, description] = paramMatches; (_d = res.returns) !== null && _d !== void 0 ? _d : (res.returns = []); res.returns.push({ param, description }); } } if (tag === 'inheritdoc') { if (!(context instanceof SourceFunction)) { throw new Error('@inheritdoc only supported in functions'); } const parentContract = context.contract.file.contractsInScope[content.trim()]; inheritFrom = parentContract.functions.find(f => f.name === context.name); } if (tag === null || tag === void 0 ? void 0 : tag.startsWith('custom:')) { const key = tag.replace(/^custom:/, ''); (_e = res.custom) !== null && _e !== void 0 ? _e : (res.custom = {}); (_f = (_g = res.custom)[key]) !== null && _f !== void 0 ? _f : (_g[key] = ''); res.custom[key] += content; } } if (inheritFrom) { lodash_1.defaults(res, inheritFrom.natspec); } return res; } function* execall(re, text) { re = new RegExp(re, re.flags + (re.sticky ? '' : 'y')); while (true) { const match = re.exec(text); // we break out of the loop if the empty string is matched because no // progress will be made and it will loop infinitely if (match && match[0] !== '') { yield match; } else { break; } } } function isVariableDeclaration(node) { return node.nodeType === 'VariableDeclaration'; } function isFunctionDefinition(node) { return node.nodeType === 'FunctionDefinition'; } function isEventDefinition(node) { return node.nodeType === 'EventDefinition'; } function isModifierDefinition(node) { return node.nodeType === 'ModifierDefinition'; } function isContractDefinition(node) { return node.nodeType === 'ContractDefinition'; } function isImportDirective(node) { return node.nodeType === 'ImportDirective'; } function oneTimeLogger(msg) { let warned = false; return function () { if (!warned) { console.warn(msg); warned = true; } }; } const warnStateVariableNatspec = oneTimeLogger('Warning: NatSpec is currently not available for state variables.'); //# sourceMappingURL=source.js.map