soliddgen 0.6.0-beta.36

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/dist/common/helpers.d.ts +9 -0
  4. package/dist/common/helpers.d.ts.map +1 -0
  5. package/dist/common/helpers.js +25 -0
  6. package/dist/common/helpers.js.map +1 -0
  7. package/dist/common/properties.d.ts +25 -0
  8. package/dist/common/properties.d.ts.map +1 -0
  9. package/dist/common/properties.js +129 -0
  10. package/dist/common/properties.js.map +1 -0
  11. package/dist/config.d.ts +59 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +14 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/doc-item.d.ts +6 -0
  16. package/dist/doc-item.d.ts.map +1 -0
  17. package/dist/doc-item.js +21 -0
  18. package/dist/doc-item.js.map +1 -0
  19. package/dist/hardhat/index.d.ts +2 -0
  20. package/dist/hardhat/index.d.ts.map +1 -0
  21. package/dist/hardhat/index.js +50 -0
  22. package/dist/hardhat/index.js.map +1 -0
  23. package/dist/hardhat/type-extensions.d.ts +12 -0
  24. package/dist/hardhat/type-extensions.d.ts.map +1 -0
  25. package/dist/hardhat/type-extensions.js +5 -0
  26. package/dist/hardhat/type-extensions.js.map +1 -0
  27. package/dist/index.d.ts +5 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +15 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/main.d.ts +9 -0
  32. package/dist/main.d.ts.map +1 -0
  33. package/dist/main.js +30 -0
  34. package/dist/main.js.map +1 -0
  35. package/dist/render.d.ts +9 -0
  36. package/dist/render.d.ts.map +1 -0
  37. package/dist/render.js +66 -0
  38. package/dist/render.js.map +1 -0
  39. package/dist/render.test.d.ts +2 -0
  40. package/dist/render.test.d.ts.map +1 -0
  41. package/dist/render.test.js +42 -0
  42. package/dist/render.test.js.map +1 -0
  43. package/dist/site.d.ts +42 -0
  44. package/dist/site.d.ts.map +1 -0
  45. package/dist/site.js +86 -0
  46. package/dist/site.js.map +1 -0
  47. package/dist/site.test.d.ts +2 -0
  48. package/dist/site.test.d.ts.map +1 -0
  49. package/dist/site.test.js +52 -0
  50. package/dist/site.test.js.map +1 -0
  51. package/dist/templates.d.ts +23 -0
  52. package/dist/templates.d.ts.map +1 -0
  53. package/dist/templates.js +124 -0
  54. package/dist/templates.js.map +1 -0
  55. package/dist/themes/markdown/helpers.d.ts +21 -0
  56. package/dist/themes/markdown/helpers.d.ts.map +1 -0
  57. package/dist/themes/markdown/helpers.js +49 -0
  58. package/dist/themes/markdown/helpers.js.map +1 -0
  59. package/dist/utils/ItemError.d.ts +5 -0
  60. package/dist/utils/ItemError.d.ts.map +1 -0
  61. package/dist/utils/ItemError.js +18 -0
  62. package/dist/utils/ItemError.js.map +1 -0
  63. package/dist/utils/arrays-equal.d.ts +3 -0
  64. package/dist/utils/arrays-equal.d.ts.map +1 -0
  65. package/dist/utils/arrays-equal.js +8 -0
  66. package/dist/utils/arrays-equal.js.map +1 -0
  67. package/dist/utils/assert-equal-types.d.ts +2 -0
  68. package/dist/utils/assert-equal-types.d.ts.map +1 -0
  69. package/dist/utils/assert-equal-types.js +3 -0
  70. package/dist/utils/assert-equal-types.js.map +1 -0
  71. package/dist/utils/clone.d.ts +7 -0
  72. package/dist/utils/clone.d.ts.map +1 -0
  73. package/dist/utils/clone.js +11 -0
  74. package/dist/utils/clone.js.map +1 -0
  75. package/dist/utils/ensure-array.d.ts +4 -0
  76. package/dist/utils/ensure-array.d.ts.map +1 -0
  77. package/dist/utils/ensure-array.js +13 -0
  78. package/dist/utils/ensure-array.js.map +1 -0
  79. package/dist/utils/execall.d.ts +7 -0
  80. package/dist/utils/execall.d.ts.map +1 -0
  81. package/dist/utils/execall.js +21 -0
  82. package/dist/utils/execall.js.map +1 -0
  83. package/dist/utils/fs-exists.d.ts +4 -0
  84. package/dist/utils/fs-exists.d.ts.map +1 -0
  85. package/dist/utils/fs-exists.js +31 -0
  86. package/dist/utils/fs-exists.js.map +1 -0
  87. package/dist/utils/is-child.d.ts +2 -0
  88. package/dist/utils/is-child.d.ts.map +1 -0
  89. package/dist/utils/is-child.js +12 -0
  90. package/dist/utils/is-child.js.map +1 -0
  91. package/dist/utils/item-type.d.ts +3 -0
  92. package/dist/utils/item-type.d.ts.map +1 -0
  93. package/dist/utils/item-type.js +10 -0
  94. package/dist/utils/item-type.js.map +1 -0
  95. package/dist/utils/map-keys.d.ts +2 -0
  96. package/dist/utils/map-keys.d.ts.map +1 -0
  97. package/dist/utils/map-keys.js +8 -0
  98. package/dist/utils/map-keys.js.map +1 -0
  99. package/dist/utils/map-values.d.ts +4 -0
  100. package/dist/utils/map-values.d.ts.map +1 -0
  101. package/dist/utils/map-values.js +22 -0
  102. package/dist/utils/map-values.js.map +1 -0
  103. package/dist/utils/memoized-getter.d.ts +4 -0
  104. package/dist/utils/memoized-getter.d.ts.map +1 -0
  105. package/dist/utils/memoized-getter.js +25 -0
  106. package/dist/utils/memoized-getter.js.map +1 -0
  107. package/dist/utils/natspec.d.ts +19 -0
  108. package/dist/utils/natspec.d.ts.map +1 -0
  109. package/dist/utils/natspec.js +117 -0
  110. package/dist/utils/natspec.js.map +1 -0
  111. package/dist/utils/read-item-docs.d.ts +3 -0
  112. package/dist/utils/read-item-docs.d.ts.map +1 -0
  113. package/dist/utils/read-item-docs.js +29 -0
  114. package/dist/utils/read-item-docs.js.map +1 -0
  115. package/dist/utils/scope.d.ts +6 -0
  116. package/dist/utils/scope.d.ts.map +1 -0
  117. package/dist/utils/scope.js +53 -0
  118. package/dist/utils/scope.js.map +1 -0
  119. package/dist/utils/test.d.ts +8 -0
  120. package/dist/utils/test.d.ts.map +1 -0
  121. package/dist/utils/test.js +14 -0
  122. package/dist/utils/test.js.map +1 -0
  123. package/lc3cxy2s.cjs +1 -0
  124. package/package.json +41 -0
  125. package/src/common/helpers.ts +22 -0
  126. package/src/common/properties.ts +138 -0
  127. package/src/config.ts +84 -0
  128. package/src/doc-item.ts +27 -0
  129. package/src/hardhat/index.ts +35 -0
  130. package/src/hardhat/type-extensions.ts +14 -0
  131. package/src/index.ts +13 -0
  132. package/src/main.ts +26 -0
  133. package/src/render.test.ts +64 -0
  134. package/src/render.ts +87 -0
  135. package/src/site.test.ts +68 -0
  136. package/src/site.ts +144 -0
  137. package/src/templates.ts +116 -0
  138. package/src/themes/markdown/common.hbs +34 -0
  139. package/src/themes/markdown/contract.hbs +8 -0
  140. package/src/themes/markdown/enum.hbs +9 -0
  141. package/src/themes/markdown/error.hbs +1 -0
  142. package/src/themes/markdown/event.hbs +1 -0
  143. package/src/themes/markdown/function.hbs +1 -0
  144. package/src/themes/markdown/helpers.ts +49 -0
  145. package/src/themes/markdown/modifier.hbs +1 -0
  146. package/src/themes/markdown/page.hbs +8 -0
  147. package/src/themes/markdown/struct.hbs +9 -0
  148. package/src/themes/markdown/user-defined-value-type.hbs +1 -0
  149. package/src/themes/markdown/variable.hbs +1 -0
  150. package/src/utils/ItemError.ts +13 -0
  151. package/src/utils/arrays-equal.ts +5 -0
  152. package/src/utils/assert-equal-types.ts +1 -0
  153. package/src/utils/clone.ts +6 -0
  154. package/src/utils/ensure-array.ts +12 -0
  155. package/src/utils/execall.ts +18 -0
  156. package/src/utils/fs-exists.ts +23 -0
  157. package/src/utils/is-child.ts +5 -0
  158. package/src/utils/item-type.ts +7 -0
  159. package/src/utils/map-keys.ts +4 -0
  160. package/src/utils/map-values.ts +19 -0
  161. package/src/utils/memoized-getter.ts +23 -0
  162. package/src/utils/natspec.ts +145 -0
  163. package/src/utils/read-item-docs.ts +26 -0
  164. package/src/utils/scope.ts +63 -0
  165. 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;