soliddoc 0.6.0-beta.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/common/helpers.d.ts +9 -0
- package/dist/common/helpers.d.ts.map +1 -0
- package/dist/common/helpers.js +25 -0
- package/dist/common/helpers.js.map +1 -0
- package/dist/common/properties.d.ts +25 -0
- package/dist/common/properties.d.ts.map +1 -0
- package/dist/common/properties.js +129 -0
- package/dist/common/properties.js.map +1 -0
- package/dist/config.d.ts +59 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -0
- package/dist/doc-item.d.ts +6 -0
- package/dist/doc-item.d.ts.map +1 -0
- package/dist/doc-item.js +21 -0
- package/dist/doc-item.js.map +1 -0
- package/dist/hardhat/index.d.ts +2 -0
- package/dist/hardhat/index.d.ts.map +1 -0
- package/dist/hardhat/index.js +50 -0
- package/dist/hardhat/index.js.map +1 -0
- package/dist/hardhat/type-extensions.d.ts +12 -0
- package/dist/hardhat/type-extensions.d.ts.map +1 -0
- package/dist/hardhat/type-extensions.js +5 -0
- package/dist/hardhat/type-extensions.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +9 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +30 -0
- package/dist/main.js.map +1 -0
- package/dist/render.d.ts +9 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +66 -0
- package/dist/render.js.map +1 -0
- package/dist/render.test.d.ts +2 -0
- package/dist/render.test.d.ts.map +1 -0
- package/dist/render.test.js +42 -0
- package/dist/render.test.js.map +1 -0
- package/dist/site.d.ts +42 -0
- package/dist/site.d.ts.map +1 -0
- package/dist/site.js +86 -0
- package/dist/site.js.map +1 -0
- package/dist/site.test.d.ts +2 -0
- package/dist/site.test.d.ts.map +1 -0
- package/dist/site.test.js +52 -0
- package/dist/site.test.js.map +1 -0
- package/dist/templates.d.ts +23 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +124 -0
- package/dist/templates.js.map +1 -0
- package/dist/themes/markdown/helpers.d.ts +21 -0
- package/dist/themes/markdown/helpers.d.ts.map +1 -0
- package/dist/themes/markdown/helpers.js +49 -0
- package/dist/themes/markdown/helpers.js.map +1 -0
- package/dist/utils/ItemError.d.ts +5 -0
- package/dist/utils/ItemError.d.ts.map +1 -0
- package/dist/utils/ItemError.js +18 -0
- package/dist/utils/ItemError.js.map +1 -0
- package/dist/utils/arrays-equal.d.ts +3 -0
- package/dist/utils/arrays-equal.d.ts.map +1 -0
- package/dist/utils/arrays-equal.js +8 -0
- package/dist/utils/arrays-equal.js.map +1 -0
- package/dist/utils/assert-equal-types.d.ts +2 -0
- package/dist/utils/assert-equal-types.d.ts.map +1 -0
- package/dist/utils/assert-equal-types.js +3 -0
- package/dist/utils/assert-equal-types.js.map +1 -0
- package/dist/utils/clone.d.ts +7 -0
- package/dist/utils/clone.d.ts.map +1 -0
- package/dist/utils/clone.js +11 -0
- package/dist/utils/clone.js.map +1 -0
- package/dist/utils/ensure-array.d.ts +4 -0
- package/dist/utils/ensure-array.d.ts.map +1 -0
- package/dist/utils/ensure-array.js +13 -0
- package/dist/utils/ensure-array.js.map +1 -0
- package/dist/utils/execall.d.ts +7 -0
- package/dist/utils/execall.d.ts.map +1 -0
- package/dist/utils/execall.js +21 -0
- package/dist/utils/execall.js.map +1 -0
- package/dist/utils/fs-exists.d.ts +4 -0
- package/dist/utils/fs-exists.d.ts.map +1 -0
- package/dist/utils/fs-exists.js +31 -0
- package/dist/utils/fs-exists.js.map +1 -0
- package/dist/utils/is-child.d.ts +2 -0
- package/dist/utils/is-child.d.ts.map +1 -0
- package/dist/utils/is-child.js +12 -0
- package/dist/utils/is-child.js.map +1 -0
- package/dist/utils/item-type.d.ts +3 -0
- package/dist/utils/item-type.d.ts.map +1 -0
- package/dist/utils/item-type.js +10 -0
- package/dist/utils/item-type.js.map +1 -0
- package/dist/utils/map-keys.d.ts +2 -0
- package/dist/utils/map-keys.d.ts.map +1 -0
- package/dist/utils/map-keys.js +8 -0
- package/dist/utils/map-keys.js.map +1 -0
- package/dist/utils/map-values.d.ts +4 -0
- package/dist/utils/map-values.d.ts.map +1 -0
- package/dist/utils/map-values.js +22 -0
- package/dist/utils/map-values.js.map +1 -0
- package/dist/utils/memoized-getter.d.ts +4 -0
- package/dist/utils/memoized-getter.d.ts.map +1 -0
- package/dist/utils/memoized-getter.js +25 -0
- package/dist/utils/memoized-getter.js.map +1 -0
- package/dist/utils/natspec.d.ts +19 -0
- package/dist/utils/natspec.d.ts.map +1 -0
- package/dist/utils/natspec.js +117 -0
- package/dist/utils/natspec.js.map +1 -0
- package/dist/utils/read-item-docs.d.ts +3 -0
- package/dist/utils/read-item-docs.d.ts.map +1 -0
- package/dist/utils/read-item-docs.js +29 -0
- package/dist/utils/read-item-docs.js.map +1 -0
- package/dist/utils/scope.d.ts +6 -0
- package/dist/utils/scope.d.ts.map +1 -0
- package/dist/utils/scope.js +53 -0
- package/dist/utils/scope.js.map +1 -0
- package/dist/utils/test.d.ts +8 -0
- package/dist/utils/test.d.ts.map +1 -0
- package/dist/utils/test.js +14 -0
- package/dist/utils/test.js.map +1 -0
- package/iv6gcs3z.cjs +1 -0
- package/package.json +41 -0
- package/src/common/helpers.ts +22 -0
- package/src/common/properties.ts +138 -0
- package/src/config.ts +84 -0
- package/src/doc-item.ts +27 -0
- package/src/hardhat/index.ts +35 -0
- package/src/hardhat/type-extensions.ts +14 -0
- package/src/index.ts +13 -0
- package/src/main.ts +26 -0
- package/src/render.test.ts +64 -0
- package/src/render.ts +87 -0
- package/src/site.test.ts +68 -0
- package/src/site.ts +144 -0
- package/src/templates.ts +116 -0
- package/src/themes/markdown/common.hbs +34 -0
- package/src/themes/markdown/contract.hbs +8 -0
- package/src/themes/markdown/enum.hbs +9 -0
- package/src/themes/markdown/error.hbs +1 -0
- package/src/themes/markdown/event.hbs +1 -0
- package/src/themes/markdown/function.hbs +1 -0
- package/src/themes/markdown/helpers.ts +49 -0
- package/src/themes/markdown/modifier.hbs +1 -0
- package/src/themes/markdown/page.hbs +8 -0
- package/src/themes/markdown/struct.hbs +9 -0
- package/src/themes/markdown/user-defined-value-type.hbs +1 -0
- package/src/themes/markdown/variable.hbs +1 -0
- package/src/utils/ItemError.ts +13 -0
- package/src/utils/arrays-equal.ts +5 -0
- package/src/utils/assert-equal-types.ts +1 -0
- package/src/utils/clone.ts +6 -0
- package/src/utils/ensure-array.ts +12 -0
- package/src/utils/execall.ts +18 -0
- package/src/utils/fs-exists.ts +23 -0
- package/src/utils/is-child.ts +5 -0
- package/src/utils/item-type.ts +7 -0
- package/src/utils/map-keys.ts +4 -0
- package/src/utils/map-values.ts +19 -0
- package/src/utils/memoized-getter.ts +23 -0
- package/src/utils/natspec.ts +145 -0
- package/src/utils/read-item-docs.ts +26 -0
- package/src/utils/scope.ts +63 -0
- package/src/utils/test.ts +18 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { FunctionDefinition } from 'solidity-ast';
|
|
2
|
+
import { findAll } from 'solidity-ast/utils';
|
|
3
|
+
import { DocItemWithContext, DOC_ITEM_CONTEXT } from '../site';
|
|
4
|
+
import { arraysEqual } from './arrays-equal';
|
|
5
|
+
import { execAll } from './execall';
|
|
6
|
+
import { itemType } from './item-type';
|
|
7
|
+
import { ItemError } from './ItemError';
|
|
8
|
+
import { readItemDocs } from './read-item-docs';
|
|
9
|
+
import { getContractsInScope } from './scope';
|
|
10
|
+
|
|
11
|
+
export interface NatSpec {
|
|
12
|
+
title?: string;
|
|
13
|
+
notice?: string;
|
|
14
|
+
dev?: string;
|
|
15
|
+
params?: {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
}[];
|
|
19
|
+
returns?: {
|
|
20
|
+
name?: string;
|
|
21
|
+
description: string;
|
|
22
|
+
}[];
|
|
23
|
+
custom?: {
|
|
24
|
+
[tag: string]: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function parseNatspec(item: DocItemWithContext): NatSpec {
|
|
29
|
+
if (!item[DOC_ITEM_CONTEXT]) throw new Error(`Not an item or item is missing context`);
|
|
30
|
+
|
|
31
|
+
let res: NatSpec = {};
|
|
32
|
+
|
|
33
|
+
const docSource = readItemDocs(item);
|
|
34
|
+
const docString = docSource !== undefined
|
|
35
|
+
? cleanUpDocstringFromSource(docSource)
|
|
36
|
+
: 'documentation' in item && item.documentation
|
|
37
|
+
? typeof item.documentation === 'string'
|
|
38
|
+
? item.documentation
|
|
39
|
+
: cleanUpDocstringFromSolc(item.documentation.text)
|
|
40
|
+
: '';
|
|
41
|
+
|
|
42
|
+
const tagMatches = execAll(
|
|
43
|
+
/^(?:@(\w+|custom:[a-z][a-z-]*) )?((?:(?!^@(?:\w+|custom:[a-z][a-z-]*) )[^])*)/m,
|
|
44
|
+
docString,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
let inheritFrom: FunctionDefinition | undefined;
|
|
48
|
+
|
|
49
|
+
for (const [, tag = 'notice', content] of tagMatches) {
|
|
50
|
+
if (content === undefined) throw new ItemError('Unexpected error', item);
|
|
51
|
+
|
|
52
|
+
if (tag === 'dev' || tag === 'notice') {
|
|
53
|
+
res[tag] ??= '';
|
|
54
|
+
res[tag] += content;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (tag === 'title') {
|
|
58
|
+
res.title = content.trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (tag === 'param') {
|
|
62
|
+
const paramMatches = content.match(/(\w+) ([^]*)/);
|
|
63
|
+
if (paramMatches) {
|
|
64
|
+
const [, name, description] = paramMatches as [string, string, string];
|
|
65
|
+
res.params ??= [];
|
|
66
|
+
res.params.push({ name, description: description.trim() });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (tag === 'return') {
|
|
71
|
+
if (!('returnParameters' in item)) {
|
|
72
|
+
throw new ItemError(`Item does not contain return parameters`, item);
|
|
73
|
+
}
|
|
74
|
+
res.returns ??= [];
|
|
75
|
+
const i = res.returns.length;
|
|
76
|
+
const p = item.returnParameters.parameters[i];
|
|
77
|
+
if (p === undefined) {
|
|
78
|
+
throw new ItemError('Got more @return tags than expected', item);
|
|
79
|
+
}
|
|
80
|
+
if (!p.name) {
|
|
81
|
+
res.returns.push({ description: content.trim() });
|
|
82
|
+
} else {
|
|
83
|
+
const paramMatches = content.match(/(\w+)( ([^]*))?/);
|
|
84
|
+
if (!paramMatches || paramMatches[1] !== p.name) {
|
|
85
|
+
throw new ItemError(`Expected @return tag to start with name '${p.name}'`, item);
|
|
86
|
+
}
|
|
87
|
+
const [, name, description] = paramMatches as [string, string, string?];
|
|
88
|
+
res.returns.push({ name, description: description?.trim() ?? '' });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (tag?.startsWith('custom:')) {
|
|
93
|
+
const key = tag.replace(/^custom:/, '');
|
|
94
|
+
res.custom ??= {};
|
|
95
|
+
res.custom[key] ??= '';
|
|
96
|
+
res.custom[key] += content;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (tag === 'inheritdoc') {
|
|
100
|
+
if (!(item.nodeType === 'FunctionDefinition' || item.nodeType === 'VariableDeclaration')) {
|
|
101
|
+
throw new ItemError(`Expected function or variable but saw ${itemType(item)}`, item);
|
|
102
|
+
}
|
|
103
|
+
const parentContractName = content.trim();
|
|
104
|
+
const parentContract = getContractsInScope(item)[parentContractName];
|
|
105
|
+
if (!parentContract) {
|
|
106
|
+
throw new ItemError(`Parent contract '${parentContractName}' not found`, item);
|
|
107
|
+
}
|
|
108
|
+
inheritFrom = [...findAll('FunctionDefinition', parentContract)].find(f => item.baseFunctions?.includes(f.id));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (docString.length === 0) {
|
|
113
|
+
if ('baseFunctions' in item && item.baseFunctions?.length === 1) {
|
|
114
|
+
const baseFn = item[DOC_ITEM_CONTEXT].build.deref('FunctionDefinition', item.baseFunctions[0]!);
|
|
115
|
+
const shouldInherit = item.nodeType === 'VariableDeclaration' || arraysEqual(item.parameters.parameters, baseFn.parameters.parameters, p => p.name);
|
|
116
|
+
if (shouldInherit) {
|
|
117
|
+
inheritFrom = baseFn;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (res.dev) res.dev = res.dev.trim();
|
|
123
|
+
if (res.notice) res.notice = res.notice.trim();
|
|
124
|
+
|
|
125
|
+
if (inheritFrom) {
|
|
126
|
+
res = { ...parseNatspec(inheritFrom as DocItemWithContext), ...res };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return res;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Fix solc buggy parsing of doc comments.
|
|
133
|
+
// Reverse engineered from solc behavior.
|
|
134
|
+
function cleanUpDocstringFromSolc(text: string) {
|
|
135
|
+
return text
|
|
136
|
+
.replace(/\n\n?^[ \t]*(?:\*|\/\/\/)/mg, '\n\n')
|
|
137
|
+
.replace(/^[ \t]?/mg, '');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function cleanUpDocstringFromSource(text: string) {
|
|
141
|
+
return text
|
|
142
|
+
.replace(/^\/\*\*(.*)\*\/$/s, '$1')
|
|
143
|
+
.trim()
|
|
144
|
+
.replace(/^[ \t]*(\*|\/\/\/)[ \t]?/mg, '');
|
|
145
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DocItemWithContext, DOC_ITEM_CONTEXT, Build } from '../site';
|
|
2
|
+
|
|
3
|
+
export function readItemDocs(item: DocItemWithContext): string | undefined {
|
|
4
|
+
const { build } = item[DOC_ITEM_CONTEXT];
|
|
5
|
+
// Note that Solidity 0.5 has item.documentation: string even though the
|
|
6
|
+
// types do not reflect that. This is why we check typeof === object.
|
|
7
|
+
if ('documentation' in item && item.documentation && typeof item.documentation === 'object') {
|
|
8
|
+
const { source, start, length } = decodeSrc(item.documentation.src, build);
|
|
9
|
+
const content = build.input.sources[source]?.content;
|
|
10
|
+
if (content !== undefined) {
|
|
11
|
+
return Buffer.from(content, 'utf8').slice(start, start + length).toString('utf8');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function decodeSrc(src: string, build: Build): { source: string; start: number; length: number } {
|
|
17
|
+
const [start, length, sourceId] = src.split(':').map(s => parseInt(s));
|
|
18
|
+
if (start === undefined || length === undefined || sourceId === undefined) {
|
|
19
|
+
throw new Error(`Bad source string ${src}`);
|
|
20
|
+
}
|
|
21
|
+
const source = Object.keys(build.output.sources).find(s => build.output.sources[s]?.id === sourceId);
|
|
22
|
+
if (source === undefined) {
|
|
23
|
+
throw new Error(`No source with id ${sourceId}`);
|
|
24
|
+
}
|
|
25
|
+
return { source, start, length };
|
|
26
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ContractDefinition, SourceUnit } from "solidity-ast";
|
|
2
|
+
import { findAll, isNodeType } from "solidity-ast/utils";
|
|
3
|
+
import { DocItemWithContext } from "../site";
|
|
4
|
+
import { filterValues, mapValues } from './map-values';
|
|
5
|
+
import { mapKeys } from './map-keys';
|
|
6
|
+
|
|
7
|
+
type Definition = SourceUnit['nodes'][number] & { name: string };
|
|
8
|
+
type Scope = { [name in string]: () => { namespace: Scope } | { definition: Definition } };
|
|
9
|
+
|
|
10
|
+
export function getContractsInScope(item: DocItemWithContext) {
|
|
11
|
+
const cache = new WeakMap<SourceUnit, Scope>();
|
|
12
|
+
|
|
13
|
+
return filterValues(
|
|
14
|
+
flattenScope(run(item.__item_context.file)),
|
|
15
|
+
isNodeType('ContractDefinition'),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
function run(file: SourceUnit): Scope {
|
|
19
|
+
if (cache.has(file)) {
|
|
20
|
+
return cache.get(file)!;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const scope: Scope = {};
|
|
24
|
+
|
|
25
|
+
cache.set(file, scope);
|
|
26
|
+
|
|
27
|
+
for (const c of file.nodes) {
|
|
28
|
+
if ('name' in c) {
|
|
29
|
+
scope[c.name] = () => ({ definition: c });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const i of findAll('ImportDirective', file)) {
|
|
34
|
+
const importedFile = item.__item_context.build.deref('SourceUnit', i.sourceUnit);
|
|
35
|
+
const importedScope = run(importedFile);
|
|
36
|
+
if (i.unitAlias) {
|
|
37
|
+
scope[i.unitAlias] = () => ({ namespace: importedScope });
|
|
38
|
+
} else if (i.symbolAliases.length === 0) {
|
|
39
|
+
Object.assign(scope, importedScope);
|
|
40
|
+
} else {
|
|
41
|
+
for (const a of i.symbolAliases) {
|
|
42
|
+
// Delayed function call supports circular dependencies
|
|
43
|
+
scope[a.local ?? a.foreign.name] = importedScope[a.foreign.name] ?? (() => importedScope[a.foreign.name]!());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return scope;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function flattenScope(scope: Scope): Record<string, Definition> {
|
|
53
|
+
return Object.fromEntries(
|
|
54
|
+
Object.entries(scope).flatMap(([k, fn]) => {
|
|
55
|
+
const v = fn();
|
|
56
|
+
if ('definition' in v) {
|
|
57
|
+
return [[k, v.definition] as const];
|
|
58
|
+
} else {
|
|
59
|
+
return Object.entries(mapKeys(flattenScope(v.namespace), k2 => k + '.' + k2));
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import _test, { TestFn } from 'ava';
|
|
2
|
+
import hre from 'hardhat';
|
|
3
|
+
import { BuildInfo } from 'hardhat/types';
|
|
4
|
+
import { promises as fs } from 'fs';
|
|
5
|
+
|
|
6
|
+
interface Context {
|
|
7
|
+
build: BuildInfo[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const test = _test as TestFn<Context>;
|
|
11
|
+
|
|
12
|
+
test.before('reading build info', async t => {
|
|
13
|
+
t.context.build = await Promise.all(
|
|
14
|
+
(await hre.artifacts.getBuildInfoPaths()).map(async p => JSON.parse(await fs.readFile(p, 'utf8')))
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default test;
|