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.
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/iv6gcs3z.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
package/src/site.ts ADDED
@@ -0,0 +1,144 @@
1
+ import path from 'path';
2
+ import { ContractDefinition, SourceUnit } from 'solidity-ast';
3
+ import { SolcOutput, SolcInput } from 'solidity-ast/solc';
4
+ import { astDereferencer, ASTDereferencer, findAll, isNodeType, srcDecoder, SrcDecoder } from 'solidity-ast/utils';
5
+ import { FullConfig } from './config';
6
+ import { DocItem, docItemTypes, isDocItem } from './doc-item';
7
+ import { Properties } from './templates';
8
+ import { clone } from './utils/clone';
9
+ import { isChild } from './utils/is-child';
10
+ import { mapValues } from './utils/map-values';
11
+ import { defineGetterMemoized } from './utils/memoized-getter';
12
+
13
+ export interface Build {
14
+ input: SolcInput;
15
+ output: SolcOutput;
16
+ }
17
+
18
+ export interface BuildContext extends Build {
19
+ deref: ASTDereferencer;
20
+ decodeSrc: SrcDecoder;
21
+ }
22
+
23
+ export type SiteConfig = Pick<FullConfig, 'pages' | 'exclude' | 'sourcesDir' | 'pageExtension'>;
24
+ export type PageStructure = SiteConfig['pages'];
25
+ export type PageAssigner = ((item: DocItem, file: SourceUnit, config: SiteConfig) => string | undefined);
26
+
27
+ export const pageAssigner: Record<PageStructure & string, PageAssigner> = {
28
+ single: (_1, _2, { pageExtension: ext }) => 'index' + ext,
29
+ items: (item, _, { pageExtension: ext }) => item.name + ext,
30
+ files: (_, file, { pageExtension: ext, sourcesDir }) =>
31
+ path.relative(sourcesDir, file.absolutePath).replace('.sol', ext),
32
+ };
33
+
34
+ export interface Site {
35
+ items: DocItemWithContext[];
36
+ pages: Page[];
37
+ }
38
+
39
+ export interface Page {
40
+ id: string;
41
+ items: DocItemWithContext[];
42
+ }
43
+
44
+ export const DOC_ITEM_CONTEXT = '__item_context' as const;
45
+ export type DocItemWithContext = DocItem & { [DOC_ITEM_CONTEXT]: DocItemContext };
46
+
47
+ export interface DocItemContext {
48
+ page?: string;
49
+ item: DocItemWithContext;
50
+ contract?: ContractDefinition;
51
+ file: DocItemContextFile;
52
+ build: BuildContext;
53
+ }
54
+
55
+ export interface DocItemContextFile extends SourceUnit {
56
+ relativePath: string;
57
+ }
58
+
59
+ export function buildSite(builds: Build[], siteConfig: SiteConfig, properties: Properties = {}): Site {
60
+ const assign = typeof siteConfig.pages === 'string' ? pageAssigner[siteConfig.pages] : siteConfig.pages;
61
+
62
+ const seen = new Set<string>();
63
+ const items: DocItemWithContext[] = [];
64
+ const pages: Record<string, DocItemWithContext[]> = {};
65
+
66
+ for (let { input, output } of builds) {
67
+ // Clone because we will mutate in order to add item context.
68
+ output = { ...output, sources: clone(output.sources) };
69
+
70
+ const deref = astDereferencer(output);
71
+ const decodeSrc = srcDecoder(input, output);
72
+ const build = { input, output, deref, decodeSrc };
73
+
74
+ for (const { ast } of Object.values(output.sources)) {
75
+ const isNewFile = !seen.has(ast.absolutePath);
76
+ seen.add(ast.absolutePath);
77
+
78
+ const relativePath = path.relative(siteConfig.sourcesDir, ast.absolutePath);
79
+ const file = Object.assign(ast, { relativePath });
80
+
81
+ for (const topLevelItem of file.nodes) {
82
+ if (!isDocItem(topLevelItem)) continue;
83
+
84
+ const page = assignIfIncludedSource(assign, topLevelItem, file, siteConfig);
85
+
86
+ const withContext = defineContext(topLevelItem, build, file, page);
87
+ defineProperties(withContext, properties);
88
+
89
+ if (isNewFile && page !== undefined) {
90
+ (pages[page] ??= []).push(withContext);
91
+ items.push(withContext);
92
+ }
93
+
94
+ if (!isNodeType('ContractDefinition', topLevelItem)) {
95
+ continue;
96
+ }
97
+
98
+ for (const item of topLevelItem.nodes) {
99
+ if (!isDocItem(item)) continue;
100
+ if (isNewFile && page !== undefined) items.push(item as DocItemWithContext);
101
+ const contract = topLevelItem.nodeType === 'ContractDefinition' ? topLevelItem : undefined;
102
+ const withContext = defineContext(item, build, file, page, contract);
103
+ defineProperties(withContext, properties);
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ return {
110
+ items,
111
+ pages: Object.entries(pages).map(([id, pageItems]) => ({ id, items: pageItems })),
112
+ };
113
+ }
114
+
115
+ function defineContext(item: DocItem, build: BuildContext, file: DocItemContextFile, page?: string, contract?: ContractDefinition): DocItemWithContext {
116
+ return Object.assign(item, {
117
+ [DOC_ITEM_CONTEXT]: { build, file, contract, page, item: item as DocItemWithContext },
118
+ });
119
+ }
120
+
121
+ function defineProperties(item: DocItemWithContext, properties: Properties) {
122
+ for (const [prop, fn] of Object.entries(properties)) {
123
+ const original: unknown = (item as any)[prop];
124
+ defineGetterMemoized(item as any, prop, () => fn(item.__item_context, original));
125
+ }
126
+ }
127
+
128
+ function assignIfIncludedSource(
129
+ assign: PageAssigner,
130
+ item: DocItem,
131
+ file: DocItemContextFile,
132
+ config: SiteConfig,
133
+ ) {
134
+ return isFileIncluded(file.absolutePath, config)
135
+ ? assign(item, file, config)
136
+ : undefined;
137
+ }
138
+
139
+ function isFileIncluded(file: string, config: SiteConfig) {
140
+ return (
141
+ isChild(file, config.sourcesDir) &&
142
+ config.exclude.every(e => !isChild(file, path.join(config.sourcesDir, e)))
143
+ );
144
+ }
@@ -0,0 +1,116 @@
1
+ import path from 'path';
2
+ import fs, { promises as fsPromise } from 'fs';
3
+ import Handlebars, { RuntimeOptions } from 'handlebars';
4
+ import { findIn } from './utils/fs-exists';
5
+ import { mapValues } from './utils/map-values';
6
+ import { mapKeys } from './utils/map-keys';
7
+ import { DocItemContext } from './site';
8
+
9
+ import * as defaultProperties from './common/properties';
10
+
11
+ export type PropertyGetter = (ctx: DocItemContext, original?: unknown) => unknown;
12
+ export type Properties = Record<string, PropertyGetter>;
13
+
14
+ export interface Templates {
15
+ partials?: Record<string, () => string>;
16
+ helpers?: Record<string, (...args: unknown[]) => string>;
17
+ properties?: Record<string, PropertyGetter>;
18
+ }
19
+
20
+ /**
21
+ * Loads the templates that will be used for rendering a site based on a
22
+ * default theme and user templates.
23
+ *
24
+ * The result contains all partials, helpers, and property getters defined in
25
+ * the user templates and the default theme, where the user's take precedence
26
+ * if there is a clash. Additionally, all theme partials and helpers are
27
+ * included with the theme prefix, e.g. `markdown/contract` will be a partial.
28
+ */
29
+ export async function loadTemplates(defaultTheme: string, root: string, userTemplatesPath?: string): Promise<Templates> {
30
+ const themes = await readThemes();
31
+
32
+ // Initialize templates with the default theme.
33
+ const templates: Required<Templates> = {
34
+ partials: { ...themes[defaultTheme]?.partials },
35
+ helpers: { ...themes[defaultTheme]?.helpers },
36
+ properties: { ...defaultProperties },
37
+ };
38
+
39
+ // Overwrite default theme with user templates.
40
+ if (userTemplatesPath) {
41
+ const userTemplates = await readTemplates(path.resolve(root, userTemplatesPath));
42
+ Object.assign(templates.partials, userTemplates.partials);
43
+ Object.assign(templates.helpers, userTemplates.helpers);
44
+ Object.assign(templates.properties, userTemplates.properties);
45
+ }
46
+
47
+ // Add partials and helpers from all themes, prefixed with the theme name.
48
+ for (const [themeName, theme] of Object.entries(themes)) {
49
+ const addPrefix = (k: string) => `${themeName}/${k}`;
50
+ Object.assign(templates.partials, mapKeys(theme.partials, addPrefix));
51
+ Object.assign(templates.helpers, mapKeys(theme.helpers, addPrefix));
52
+ }
53
+
54
+ return templates;
55
+ }
56
+
57
+ /**
58
+ * Read templates and helpers from a directory.
59
+ */
60
+ export async function readTemplates(partialsDir: string, helpersDir = partialsDir): Promise<Required<Templates>> {
61
+ return {
62
+ partials: await readPartials(partialsDir),
63
+ helpers: await readHelpers(helpersDir, 'helpers'),
64
+ properties: await readHelpers(helpersDir, 'properties'),
65
+ };
66
+ }
67
+
68
+ async function readPartials(dir: string) {
69
+ const partials: NonNullable<Templates['partials']> = {};
70
+ for (const p of await fsPromise.readdir(dir)) {
71
+ const { name, ext } = path.parse(p);
72
+ if (ext === '.hbs') {
73
+ partials[name] = () => fs.readFileSync(path.join(dir, p), 'utf8');
74
+ }
75
+ }
76
+ return partials;
77
+ }
78
+
79
+ async function readHelpers(dir: string, name: string) {
80
+ let helpersPath;
81
+ try {
82
+ helpersPath = require.resolve(path.join(dir, name));
83
+ } catch {
84
+ return {};
85
+ }
86
+ const h = await import(helpersPath);
87
+ const helpers: Record<string, (...args: any[]) => any> = {};
88
+ for (const name in h) {
89
+ if (typeof h[name] === 'function') {
90
+ helpers[name] = h[name];
91
+ }
92
+ }
93
+ return helpers;
94
+ }
95
+
96
+ /**
97
+ * Reads all built-in themes into an object. Partials will always be found in
98
+ * src/themes, whereas helpers may instead be found in dist/themes if TypeScript
99
+ * can't be imported directly.
100
+ */
101
+ async function readThemes(): Promise<Record<string, Required<Templates>>> {
102
+ const themes: Record<string, Required<Templates>> = {};
103
+
104
+ // Handlebars partials are located in src and not in dist
105
+ const srcThemes = path.resolve(__dirname, '../src/themes');
106
+ const distThemes = path.resolve(__dirname, 'themes');
107
+
108
+ for (const theme of await fsPromise.readdir(srcThemes, { withFileTypes: true })) {
109
+ if (theme.isDirectory()) {
110
+ const { name } = theme;
111
+ themes[name] = await readTemplates(path.join(srcThemes, name), path.join(distThemes, name));
112
+ }
113
+ }
114
+
115
+ return themes;
116
+ }
@@ -0,0 +1,34 @@
1
+ {{h}} {{name}}
2
+
3
+ {{#if signature}}
4
+ ```solidity
5
+ {{{signature}}}
6
+ ```
7
+ {{/if}}
8
+
9
+ {{{natspec.notice}}}
10
+
11
+ {{#if natspec.dev}}
12
+ _{{{natspec.dev}}}_
13
+ {{/if}}
14
+
15
+ {{#if natspec.params}}
16
+ {{h 2}} Parameters
17
+
18
+ | Name | Type | Description |
19
+ | ---- | ---- | ----------- |
20
+ {{#each params}}
21
+ | {{name}} | {{type}} | {{{joinLines natspec}}} |
22
+ {{/each}}
23
+ {{/if}}
24
+
25
+ {{#if natspec.returns}}
26
+ {{h 2}} Return Values
27
+
28
+ | Name | Type | Description |
29
+ | ---- | ---- | ----------- |
30
+ {{#each returns}}
31
+ | {{#if name}}{{name}}{{else}}[{{@index}}]{{/if}} | {{type}} | {{{joinLines natspec}}} |
32
+ {{/each}}
33
+ {{/if}}
34
+
@@ -0,0 +1,8 @@
1
+ {{>common}}
2
+
3
+ {{#each items}}
4
+ {{#hsection}}
5
+ {{>item}}
6
+ {{/hsection}}
7
+
8
+ {{/each}}
@@ -0,0 +1,9 @@
1
+ {{>common}}
2
+
3
+ ```solidity
4
+ enum {{name}} {
5
+ {{#each members}}
6
+ {{name}}{{#unless @last}},{{/unless}}
7
+ {{/each}}
8
+ }
9
+ ```
@@ -0,0 +1 @@
1
+ {{>common}}
@@ -0,0 +1 @@
1
+ {{>common}}
@@ -0,0 +1 @@
1
+ {{>common}}
@@ -0,0 +1,49 @@
1
+ import { HelperOptions, Utils } from 'handlebars';
2
+
3
+ export * from '../../common/helpers';
4
+
5
+ /**
6
+ * Returns a Markdown heading marker. An optional number increases the heading level.
7
+ *
8
+ * Input Output
9
+ * {{h}} {{name}} # Name
10
+ * {{h 2}} {{name}} ## Name
11
+ */
12
+ export function h(opts: HelperOptions): string;
13
+ export function h(hsublevel: number, opts: HelperOptions): string;
14
+ export function h(hsublevel: number | HelperOptions, opts?: HelperOptions) {
15
+ const { hlevel } = getHLevel(hsublevel, opts);
16
+ return new Array(hlevel).fill('#').join('');
17
+ };
18
+
19
+ /**
20
+ * Delineates a section where headings should be increased by 1 or a custom number.
21
+ *
22
+ * {{#hsection}}
23
+ * {{>partial-with-headings}}
24
+ * {{/hsection}}
25
+ */
26
+ export function hsection(opts: HelperOptions): string;
27
+ export function hsection(hsublevel: number, opts: HelperOptions): string;
28
+ export function hsection(this: unknown, hsublevel: number | HelperOptions, opts?: HelperOptions) {
29
+ let hlevel;
30
+ ({ hlevel, opts } = getHLevel(hsublevel, opts));
31
+ opts.data = Utils.createFrame(opts.data);
32
+ opts.data.hlevel = hlevel;
33
+ return opts.fn(this as unknown, opts);
34
+ }
35
+
36
+ /**
37
+ * Helper for dealing with the optional hsublevel argument.
38
+ */
39
+ function getHLevel(hsublevel: number | HelperOptions, opts?: HelperOptions) {
40
+ if (typeof hsublevel === 'number') {
41
+ opts = opts!;
42
+ hsublevel = Math.max(1, hsublevel);
43
+ } else {
44
+ opts = hsublevel;
45
+ hsublevel = 1;
46
+ }
47
+ const contextHLevel: number = opts.data?.hlevel ?? 0;
48
+ return { opts, hlevel: contextHLevel + hsublevel };
49
+ }
@@ -0,0 +1 @@
1
+ {{>common}}
@@ -0,0 +1,8 @@
1
+ # Solidity API
2
+
3
+ {{#each items}}
4
+ {{#hsection}}
5
+ {{>item}}
6
+ {{/hsection}}
7
+
8
+ {{/each}}
@@ -0,0 +1,9 @@
1
+ {{>common}}
2
+
3
+ ```solidity
4
+ struct {{name}} {
5
+ {{#each members}}
6
+ {{{typeName.typeDescriptions.typeString}}} {{name}};
7
+ {{/each}}
8
+ }
9
+ ```
@@ -0,0 +1 @@
1
+ {{>common}}
@@ -0,0 +1 @@
1
+ {{>common}}
@@ -0,0 +1,13 @@
1
+ import { DocItemWithContext, DOC_ITEM_CONTEXT } from '../site';
2
+
3
+ export class ItemError extends Error {
4
+ constructor(msg: string, item: DocItemWithContext) {
5
+ const ctx = item[DOC_ITEM_CONTEXT];
6
+ const src = ctx && ctx.build.decodeSrc(item);
7
+ if (src) {
8
+ super(msg + ` (${src})`);
9
+ } else {
10
+ super(msg);
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,5 @@
1
+ export function arraysEqual<T>(a: T[], b: T[]): boolean;
2
+ export function arraysEqual<T, U>(a: T[], b: T[], mapFn: (x: T) => U): boolean;
3
+ export function arraysEqual<T>(a: T[], b: T[], mapFn = (x: T) => x): boolean {
4
+ return a.length === b.length && a.every((x, i) => mapFn(x) === mapFn(b[i]!));
5
+ }
@@ -0,0 +1 @@
1
+ export type AssertEqual<T, U> = [T, U] extends [U, T] ? true : never;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Deep cloning good enough for simple objects like solc output. Types are not
3
+ * sound because the function may lose information: non-enumerable properties,
4
+ * symbols, undefined values, prototypes, etc.
5
+ */
6
+ export const clone = <T>(obj: T): T => JSON.parse(JSON.stringify(obj));
@@ -0,0 +1,12 @@
1
+ // The function below would not be correctly typed if the return type was T[]
2
+ // because T may itself be an array type and Array.isArray would not know the
3
+ // difference. Adding IfArray<T> makes sure the return type is always correct.
4
+ type IfArray<T> = T extends any[] ? T : never;
5
+
6
+ export function ensureArray<T>(x: T | T[]): T[] | IfArray<T> {
7
+ if (Array.isArray(x)) {
8
+ return x;
9
+ } else {
10
+ return [x];
11
+ }
12
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Iterates over all contiguous matches of the regular expression over the
3
+ * text. Stops as soon as the regular expression no longer matches at the
4
+ * current position.
5
+ */
6
+ export function* execAll(re: RegExp, text: string) {
7
+ re = new RegExp(re, re.flags + (re.sticky ? '' : 'y'));
8
+
9
+ while (true) {
10
+ const match = re.exec(text);
11
+
12
+ // We break out of the loop if there is no match or if the empty string is
13
+ // matched because no progress will be made and it will loop indefinitely.
14
+ if (!match?.[0]) break;
15
+
16
+ yield match;
17
+ }
18
+ }
@@ -0,0 +1,23 @@
1
+ import { promises as fs, constants } from 'fs';
2
+ import path from 'path';
3
+
4
+ export async function exists(path: string, mode: number = constants.R_OK): Promise<boolean> {
5
+ try {
6
+ await fs.access(path, mode);
7
+ return true;
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+
13
+ export async function findExists(...paths: string[]): Promise<string | undefined> {
14
+ for (const p of paths) {
15
+ if (await exists(p)) {
16
+ return p;
17
+ }
18
+ }
19
+ }
20
+
21
+ export async function findIn(f: string, dirs: string[]): Promise<string | undefined> {
22
+ return findExists(...dirs.map(d => path.resolve(d, f)));
23
+ }
@@ -0,0 +1,5 @@
1
+ import path from 'path';
2
+
3
+ export function isChild(file: string, parent: string) {
4
+ return path.normalize(file + path.sep).startsWith(path.normalize(parent + path.sep));
5
+ }
@@ -0,0 +1,7 @@
1
+ import { DocItem } from '../doc-item';
2
+
3
+ export function itemType(item: DocItem): string {
4
+ return item.nodeType
5
+ .replace(/(Definition|Declaration)$/, '')
6
+ .replace(/(\w)([A-Z])/g, '$1 $2');
7
+ }
@@ -0,0 +1,4 @@
1
+ export function mapKeys<T>(obj: Record<string, T>, fn: (key: string) => string): Record<string, T> {
2
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [fn(k), v]));
3
+ }
4
+
@@ -0,0 +1,19 @@
1
+ export function mapValues<T, U>(obj: Record<string, T>, fn: (value: T) => U): Record<string, U> {
2
+ const res: Record<string, U> = {};
3
+ for (const [k, v] of Object.entries(obj)) {
4
+ res[k] = fn(v);
5
+ }
6
+ return res;
7
+ }
8
+
9
+ export function filterValues<T, U extends T>(obj: Record<string, T>, fn: (value: T) => value is U): Record<string, U>;
10
+ export function filterValues<T>(obj: Record<string, T>, fn: (value: T) => boolean): Record<string, T>;
11
+ export function filterValues<T>(obj: Record<string, T>, fn: (value: T) => boolean): Record<string, T> {
12
+ const res: Record<string, T> = {};
13
+ for (const [k, v] of Object.entries(obj)) {
14
+ if (fn(v)) {
15
+ res[k] = v;
16
+ }
17
+ }
18
+ return res;
19
+ }
@@ -0,0 +1,23 @@
1
+ export function defineGetterMemoized<K extends keyof any, T, O extends { [k in K]?: T }>(obj: O, key: K, getter: () => T) {
2
+ let state: 'todo' | 'doing' | 'done' = 'todo';
3
+ let value: T;
4
+
5
+ Object.defineProperty(obj, key, {
6
+ enumerable: true,
7
+ get() {
8
+ switch (state) {
9
+ case 'done':
10
+ return value;
11
+
12
+ case 'doing':
13
+ throw new Error("Detected recursion");
14
+
15
+ case 'todo':
16
+ state = 'doing';
17
+ value = getter();
18
+ state = 'done';
19
+ return value;
20
+ }
21
+ }
22
+ });
23
+ }