solidity-docgen
Advanced tools
Comparing version 0.6.0-beta.3 to 0.6.0-beta.4
@@ -6,19 +6,25 @@ import { SourceUnit } from 'solidity-ast'; | ||
* The directory where rendered pages will be written. | ||
* Defaults to 'docs'. | ||
*/ | ||
output?: string; | ||
outputDir?: string; | ||
/** | ||
* A function that returns the page assigned to a documentable item | ||
* (contract, function, etc.) given the AST node for the item and the source | ||
* unit where it is defined. | ||
* Defaults to assigning all items to an index.md file. | ||
* A directory of custom templates that should take precedence over the | ||
* theme's templates. | ||
*/ | ||
pages?: (item: DocItem, file: SourceUnit) => string | undefined; | ||
templates?: string; | ||
/** | ||
* An array of directories or built-in names that should be searched | ||
* sequentially for templates and helpers. A single string is equal to an | ||
* array with only that string. | ||
* Defaults to built-in markdown templates. | ||
* The name of the built-in templates that will be used by default. | ||
* Defaults to 'markdown'. | ||
*/ | ||
templates?: string[] | string; | ||
theme?: string; | ||
/** | ||
* The way documentable items (contracts, functions, etc.) will be organized | ||
* in pages. Built in options are: 'single' for all items in one page, and | ||
* 'items' for one page per item. More customization is possible by defining | ||
* a function that returns a page path given the AST node for the item and | ||
* the source unit where it is defined. | ||
* Defaults to 'single'. | ||
*/ | ||
pages?: 'single' | 'items' | ((item: DocItem, file: SourceUnit) => string | undefined); | ||
/** | ||
* Clean up the output by collapsing 3 or more contiguous newlines into only 2. | ||
@@ -36,3 +42,3 @@ * Enabled by default. | ||
} | ||
export declare const defaults: Required<Config>; | ||
export declare const defaults: Omit<Required<Config>, 'templates'>; | ||
//# sourceMappingURL=config.d.ts.map |
@@ -6,7 +6,7 @@ "use strict"; | ||
root: process.cwd(), | ||
output: 'docs', | ||
pages: () => 'index.md', | ||
templates: 'markdown', | ||
outputDir: 'docs', | ||
pages: 'single', | ||
theme: 'markdown', | ||
collapseNewlines: true, | ||
}; | ||
//# sourceMappingURL=config.js.map |
@@ -1,2 +0,1 @@ | ||
import { Templates } from './render'; | ||
import { Build } from './site'; | ||
@@ -10,7 +9,2 @@ import { Config } from './config'; | ||
export declare function main(builds: Build[], userConfig?: Config): Promise<void>; | ||
/** | ||
* The array of strings containing either directory paths or built-in names is | ||
* searched sequentially for templates and helpers, and merged into an object. | ||
*/ | ||
export declare function readTemplates(templates: string[], root: string): Promise<Templates>; | ||
//# sourceMappingURL=main.d.ts.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -25,3 +6,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.readTemplates = exports.main = void 0; | ||
exports.main = void 0; | ||
const path_1 = __importDefault(require("path")); | ||
@@ -31,5 +12,4 @@ const fs_1 = require("fs"); | ||
const site_1 = require("./site"); | ||
const fs_exists_1 = require("./utils/fs-exists"); | ||
const ensure_array_1 = require("./utils/ensure-array"); | ||
const config_1 = require("./config"); | ||
const templates_1 = require("./templates"); | ||
/** | ||
@@ -43,6 +23,6 @@ * Given a set of builds (i.e. solc outputs) and a user configuration, this | ||
const site = (0, site_1.buildSite)(builds, config.pages); | ||
const templates = await readTemplates((0, ensure_array_1.ensureArray)(config.templates), config.root); | ||
const templates = await (0, templates_1.loadTemplates)(config.theme, config.root, config.templates); | ||
const renderedSite = (0, render_1.render)(site, templates, config.collapseNewlines); | ||
for (const { id, contents } of renderedSite) { | ||
const outputFile = path_1.default.resolve(config.root, config.output, id); | ||
const outputFile = path_1.default.resolve(config.root, config.outputDir, id); | ||
await fs_1.promises.mkdir(path_1.default.dirname(outputFile), { recursive: true }); | ||
@@ -53,42 +33,2 @@ await fs_1.promises.writeFile(outputFile, contents); | ||
exports.main = main; | ||
/** | ||
* The array of strings containing either directory paths or built-in names is | ||
* searched sequentially for templates and helpers, and merged into an object. | ||
*/ | ||
async function readTemplates(templates, root) { | ||
const partials = {}; | ||
const helpers = {}; | ||
for (const nameOrPath of templates) { | ||
// Look in src because built-in templates are not copied to dist | ||
const templatesDir = await (0, fs_exists_1.findIn)(nameOrPath, [root, path_1.default.join(__dirname, '../src/templates')]); | ||
if (templatesDir === undefined) { | ||
throw new Error(`Templates directory '${nameOrPath}' not found`); | ||
} | ||
for (const t of await fs_1.promises.readdir(templatesDir)) { | ||
const { name, ext } = path_1.default.parse(t); | ||
if (!(name in partials) && ext === '.hbs') { | ||
partials[name] = await fs_1.promises.readFile(path_1.default.join(templatesDir, t), 'utf8'); | ||
} | ||
} | ||
const helpersDir = await (0, fs_exists_1.findIn)(nameOrPath, [root, path_1.default.join(__dirname, './templates')]); | ||
if (helpersDir === undefined) { | ||
throw new Error(`Templates directory '${nameOrPath}' not found`); | ||
} | ||
const h = await Promise.resolve().then(() => __importStar(require(path_1.default.join(helpersDir, 'helpers')))).catch(() => undefined); | ||
for (const name in h) { | ||
if (!(name in helpers) && typeof h[name] === 'function') { | ||
helpers[name] = h[name]; | ||
} | ||
} | ||
} | ||
if (partials.page === undefined) { | ||
throw new Error(`Missing 'page' template`); | ||
} | ||
return { | ||
page: partials.page, | ||
partials, | ||
helpers, | ||
}; | ||
} | ||
exports.readTemplates = readTemplates; | ||
//# sourceMappingURL=main.js.map |
import { Site, DocItemWithContext } from './site'; | ||
export interface Templates { | ||
page: string; | ||
partials?: Record<string, string>; | ||
helpers?: Record<string, (...args: unknown[]) => string>; | ||
} | ||
import { Templates } from './templates'; | ||
export interface RenderedPage { | ||
@@ -8,0 +4,0 @@ id: string; |
@@ -10,10 +10,10 @@ "use strict"; | ||
function render(site, templates, collapseNewlines) { | ||
const template = buildTemplate(templates); | ||
const rendered = []; | ||
const renderPage = buildRenderer(templates); | ||
const renderedPages = []; | ||
for (const page of site.pages) { | ||
let contents = template(page, { data: { site } }); | ||
let contents = renderPage(page, { data: { site } }); | ||
if (collapseNewlines) { | ||
contents = contents.replace(/\n{3,}/g, '\n\n'); | ||
} | ||
rendered.push({ | ||
renderedPages.push({ | ||
id: page.id, | ||
@@ -23,3 +23,3 @@ contents, | ||
} | ||
return rendered; | ||
return renderedPages; | ||
} | ||
@@ -40,14 +40,22 @@ exports.render = render; | ||
} | ||
function buildTemplate(templates) { | ||
var _a, _b; | ||
function buildRenderer(templates) { | ||
var _a, _b, _c; | ||
const pageTemplate = (_a = templates.partials) === null || _a === void 0 ? void 0 : _a.page; | ||
if (pageTemplate === undefined) { | ||
throw new Error(`Missing 'page' template`); | ||
} | ||
const H = handlebars_1.default.create(); | ||
for (const [name, body] of Object.entries((_a = templates.partials) !== null && _a !== void 0 ? _a : {})) { | ||
H.registerPartial(name, H.compile(body)); | ||
for (const [name, getBody] of Object.entries((_b = templates.partials) !== null && _b !== void 0 ? _b : {})) { | ||
let partial; | ||
H.registerPartial(name, (...args) => { | ||
partial !== null && partial !== void 0 ? partial : (partial = H.compile(getBody())); | ||
return partial(...args); | ||
}); | ||
} | ||
for (const [name, fn] of Object.entries((_b = templates.helpers) !== null && _b !== void 0 ? _b : {})) { | ||
for (const [name, fn] of Object.entries((_c = templates.helpers) !== null && _c !== void 0 ? _c : {})) { | ||
H.registerHelper(name, fn); | ||
} | ||
H.registerPartial('item', itemPartial); | ||
return H.compile(templates.page); | ||
return H.compile('{{>page}}'); | ||
} | ||
//# sourceMappingURL=render.js.map |
@@ -23,12 +23,16 @@ "use strict"; | ||
} | ||
testRender('static page', 'S08_AB', { page: 'a page' }, 'a page'); | ||
testRender('items', 'S08_AB', { page: '{{#each items}}{{name}}, {{/each}}' }, 'A, B, '); | ||
testRender('static page', 'S08_AB', { partials: { page: () => 'a page' } }, 'a page'); | ||
testRender('items', 'S08_AB', { partials: { page: () => '{{#each items}}{{name}}, {{/each}}' } }, 'A, B, '); | ||
testRender('partials', 'S08_AB', { | ||
page: '{{#each items}}{{>part}}, {{/each}}', | ||
partials: { part: '{{name}}' }, | ||
partials: { | ||
page: () => '{{#each items}}{{>part}}, {{/each}}', | ||
part: () => '{{name}}', | ||
}, | ||
}, 'A, B, '); | ||
testRender('item partial', 'S08_AB', { | ||
page: '{{#each items}}{{>item}}, {{/each}}', | ||
partials: { contract: '{{name}}' }, | ||
partials: { | ||
page: () => '{{#each items}}{{>item}}, {{/each}}', | ||
contract: () => '{{name}}', | ||
}, | ||
}, 'A, B, '); | ||
//# sourceMappingURL=render.test.js.map |
@@ -9,3 +9,4 @@ import { ContractDefinition, SourceUnit } from 'solidity-ast'; | ||
} | ||
export declare type PageAssigner = NonNullable<Config['pages']>; | ||
export declare type PageStructure = NonNullable<Config['pages']>; | ||
export declare type PageAssigner = Exclude<PageStructure, string>; | ||
export interface Site { | ||
@@ -32,3 +33,3 @@ items: DocItemWithContext[]; | ||
} | ||
export declare function buildSite(builds: Build[], assign: PageAssigner): Site; | ||
export declare function buildSite(builds: Build[], pageStructure: PageStructure): Site; | ||
//# sourceMappingURL=site.d.ts.map |
@@ -7,4 +7,9 @@ "use strict"; | ||
const clone_1 = require("./utils/clone"); | ||
function buildSite(builds, assign) { | ||
const pageAssigner = { | ||
single: () => 'index.md', | ||
items: (item) => item.name, | ||
}; | ||
function buildSite(builds, pageStructure) { | ||
var _a; | ||
const assign = typeof pageStructure === 'string' ? pageAssigner[pageStructure] : pageStructure; | ||
const seen = new Set(); | ||
@@ -11,0 +16,0 @@ const items = []; |
@@ -6,13 +6,15 @@ "use strict"; | ||
const accessors_1 = require("../accessors"); | ||
const arrays_equal_1 = require("./arrays-equal"); | ||
const execall_1 = require("./execall"); | ||
const scope_1 = require("./scope"); | ||
function parseNatspec(item) { | ||
var _a, _b, _c, _d, _e; | ||
var _f; | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
var _h; | ||
if (!item.__item_context) | ||
throw new Error(`Not an item or item is missing context`); | ||
if (!('documentation' in item && item.documentation)) | ||
return {}; | ||
let res = {}; | ||
const tagMatches = (0, execall_1.execAll)(/^(?:@(\w+|custom:[a-z][a-z-]*) )?((?:(?!^@(?:\w+|custom:[a-z][a-z-]*) )[^])*)/m, cleanUpDocstring(item.documentation.text)); | ||
const docString = 'documentation' in item && ((_a = item.documentation) === null || _a === void 0 ? void 0 : _a.text) | ||
? cleanUpDocstring(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; | ||
@@ -23,3 +25,3 @@ for (const [, tag = 'notice', content] of tagMatches) { | ||
if (tag === 'dev' || tag === 'notice') { | ||
(_a = res[tag]) !== null && _a !== void 0 ? _a : (res[tag] = ''); | ||
(_b = res[tag]) !== null && _b !== void 0 ? _b : (res[tag] = ''); | ||
res[tag] += content; | ||
@@ -34,3 +36,3 @@ } | ||
const [, name, description] = paramMatches; | ||
(_b = res.params) !== null && _b !== void 0 ? _b : (res.params = []); | ||
(_c = res.params) !== null && _c !== void 0 ? _c : (res.params = []); | ||
res.params.push({ name, description: description.trim() }); | ||
@@ -43,3 +45,3 @@ } | ||
} | ||
(_c = res.returns) !== null && _c !== void 0 ? _c : (res.returns = []); | ||
(_d = res.returns) !== null && _d !== void 0 ? _d : (res.returns = []); | ||
const i = res.returns.length; | ||
@@ -64,4 +66,4 @@ const p = item.returnParameters.parameters[i]; | ||
const key = tag.replace(/^custom:/, ''); | ||
(_d = res.custom) !== null && _d !== void 0 ? _d : (res.custom = {}); | ||
(_e = (_f = res.custom)[key]) !== null && _e !== void 0 ? _e : (_f[key] = ''); | ||
(_e = res.custom) !== null && _e !== void 0 ? _e : (res.custom = {}); | ||
(_f = (_h = res.custom)[key]) !== null && _f !== void 0 ? _f : (_h[key] = ''); | ||
res.custom[key] += content; | ||
@@ -78,5 +80,14 @@ } | ||
} | ||
inheritFrom = [...(0, utils_1.findAll)('FunctionDefinition', parentContract)].find(f => f.name === item.name); | ||
inheritFrom = [...(0, utils_1.findAll)('FunctionDefinition', parentContract)].find(f => { var _a; return (_a = item.baseFunctions) === null || _a === void 0 ? void 0 : _a.includes(f.id); }); | ||
} | ||
} | ||
if (docString.length === 0) { | ||
if ('baseFunctions' in item && ((_g = item.baseFunctions) === null || _g === void 0 ? void 0 : _g.length) === 1) { | ||
const baseFn = item.__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) | ||
@@ -89,3 +100,2 @@ res.dev = res.dev.trim(); | ||
} | ||
// [TODO] inheritance by default as specified by solidity | ||
return res; | ||
@@ -92,0 +102,0 @@ } |
{ | ||
"name": "solidity-docgen", | ||
"version": "0.6.0-beta.3", | ||
"version": "0.6.0-beta.4", | ||
"description": "Documentation generator for Solidity smart contracts.", | ||
@@ -5,0 +5,0 @@ "repository": "github:OpenZeppelin/solidity-docgen", |
import { SourceUnit } from 'solidity-ast'; | ||
import { DocItem } from './doc-item'; | ||
import { PageAssigner } from './site'; | ||
import { PageStructure } from './site'; | ||
@@ -8,22 +8,29 @@ export interface UserConfig { | ||
* The directory where rendered pages will be written. | ||
* Defaults to 'docs'. | ||
*/ | ||
output?: string; | ||
outputDir?: string; | ||
/** | ||
* A function that returns the page assigned to a documentable item | ||
* (contract, function, etc.) given the AST node for the item and the source | ||
* unit where it is defined. | ||
* Defaults to assigning all items to an index.md file. | ||
* A directory of custom templates that should take precedence over the | ||
* theme's templates. | ||
*/ | ||
pages?: (item: DocItem, file: SourceUnit) => string | undefined; | ||
templates?: string; | ||
/** | ||
* An array of directories or built-in names that should be searched | ||
* sequentially for templates and helpers. A single string is equal to an | ||
* array with only that string. | ||
* Defaults to built-in markdown templates. | ||
* The name of the built-in templates that will be used by default. | ||
* Defaults to 'markdown'. | ||
*/ | ||
templates?: string[] | string; | ||
theme?: string; | ||
/** | ||
* The way documentable items (contracts, functions, etc.) will be organized | ||
* in pages. Built in options are: 'single' for all items in one page, and | ||
* 'items' for one page per item. More customization is possible by defining | ||
* a function that returns a page path given the AST node for the item and | ||
* the source unit where it is defined. | ||
* Defaults to 'single'. | ||
*/ | ||
pages?: 'single' | 'items' | ((item: DocItem, file: SourceUnit) => string | undefined); | ||
/** | ||
* Clean up the output by collapsing 3 or more contiguous newlines into only 2. | ||
@@ -47,8 +54,8 @@ * Enabled by default. | ||
export const defaults: Required<Config> = { | ||
export const defaults: Omit<Required<Config>, 'templates'> = { | ||
root: process.cwd(), | ||
output: 'docs', | ||
pages: () => 'index.md', | ||
templates: 'markdown', | ||
outputDir: 'docs', | ||
pages: 'single', | ||
theme: 'markdown', | ||
collapseNewlines: true, | ||
}; |
import path from 'path'; | ||
import { promises as fs } from 'fs'; | ||
import { SolcOutput } from 'solidity-ast/solc'; | ||
import { render, Templates } from './render'; | ||
import { Build, PageAssigner, buildSite } from './site'; | ||
import { exists, findIn } from './utils/fs-exists'; | ||
import { render } from './render'; | ||
import { Build, buildSite } from './site'; | ||
import { ensureArray } from './utils/ensure-array'; | ||
import { Config, defaults } from './config'; | ||
import { loadTemplates } from './templates'; | ||
@@ -20,7 +19,7 @@ /** | ||
const templates = await readTemplates(ensureArray(config.templates), config.root); | ||
const templates = await loadTemplates(config.theme, config.root, config.templates); | ||
const renderedSite = render(site, templates, config.collapseNewlines); | ||
for (const { id, contents } of renderedSite) { | ||
const outputFile = path.resolve(config.root, config.output, id); | ||
const outputFile = path.resolve(config.root, config.outputDir, id); | ||
await fs.mkdir(path.dirname(outputFile), { recursive: true }); | ||
@@ -30,47 +29,1 @@ await fs.writeFile(outputFile, contents); | ||
} | ||
/** | ||
* The array of strings containing either directory paths or built-in names is | ||
* searched sequentially for templates and helpers, and merged into an object. | ||
*/ | ||
export async function readTemplates(templates: string[], root: string): Promise<Templates> { | ||
const partials: Record<string, string> = {}; | ||
const helpers: Record<string, any> = {}; | ||
for (const nameOrPath of templates) { | ||
// Look in src because built-in templates are not copied to dist | ||
const templatesDir = await findIn(nameOrPath, [root, path.join(__dirname, '../src/templates')]); | ||
if (templatesDir === undefined) { | ||
throw new Error(`Templates directory '${nameOrPath}' not found`); | ||
} | ||
for (const t of await fs.readdir(templatesDir)) { | ||
const { name, ext } = path.parse(t); | ||
if (!(name in partials) && ext === '.hbs') { | ||
partials[name] = await fs.readFile(path.join(templatesDir, t), 'utf8'); | ||
} | ||
} | ||
const helpersDir = await findIn(nameOrPath, [root, path.join(__dirname, './templates')]); | ||
if (helpersDir === undefined) { | ||
throw new Error(`Templates directory '${nameOrPath}' not found`); | ||
} | ||
const h = await import(path.join(helpersDir, 'helpers')).catch(() => undefined); | ||
for (const name in h) { | ||
if (!(name in helpers) && typeof h[name] === 'function') { | ||
helpers[name] = h[name]; | ||
} | ||
} | ||
} | ||
if (partials.page === undefined) { | ||
throw new Error(`Missing 'page' template`); | ||
} | ||
return { | ||
page: partials.page, | ||
partials, | ||
helpers, | ||
}; | ||
} |
import test from './utils/test'; | ||
import path from 'path'; | ||
import { buildSite, PageAssigner } from './site'; | ||
import { itemPartialName, render, Templates } from './render'; | ||
import { buildSite, PageStructure } from './site'; | ||
import { itemPartialName, render } from './render'; | ||
import { NodeType } from 'solidity-ast/node'; | ||
import { Templates } from './templates'; | ||
@@ -16,3 +17,3 @@ interface TestSpec extends Templates { | ||
const id = 'index.md'; | ||
const assign: PageAssigner = (_, f) => path.parse(f.absolutePath).name === file ? id : undefined; | ||
const assign: PageStructure = (_, f) => path.parse(f.absolutePath).name === file ? id : undefined; | ||
@@ -29,3 +30,3 @@ test(title, t => { | ||
'S08_AB', | ||
{ page: 'a page' }, | ||
{ partials: { page: () => 'a page' } }, | ||
'a page', | ||
@@ -36,3 +37,3 @@ ); | ||
'S08_AB', | ||
{ page: '{{#each items}}{{name}}, {{/each}}' }, | ||
{ partials: { page: () => '{{#each items}}{{name}}, {{/each}}' } }, | ||
'A, B, ', | ||
@@ -44,4 +45,6 @@ ); | ||
{ | ||
page: '{{#each items}}{{>part}}, {{/each}}', | ||
partials: { part: '{{name}}' }, | ||
partials: { | ||
page: () => '{{#each items}}{{>part}}, {{/each}}', | ||
part: () => '{{name}}', | ||
}, | ||
}, | ||
@@ -54,6 +57,8 @@ 'A, B, ', | ||
{ | ||
page: '{{#each items}}{{>item}}, {{/each}}', | ||
partials: { contract: '{{name}}' }, | ||
partials: { | ||
page: () => '{{#each items}}{{>item}}, {{/each}}', | ||
contract: () => '{{name}}', | ||
}, | ||
}, | ||
'A, B, ', | ||
); |
@@ -5,9 +5,4 @@ import Handlebars, { RuntimeOptions } from 'handlebars'; | ||
import { accessors, wrapWithAccessors } from './accessors'; | ||
import { Templates } from './templates'; | ||
export interface Templates { | ||
page: string; | ||
partials?: Record<string, string>; | ||
helpers?: Record<string, (...args: unknown[]) => string>; | ||
} | ||
export interface RenderedPage { | ||
@@ -25,10 +20,10 @@ id: string; | ||
export function render(site: Site, templates: Templates, collapseNewlines?: boolean): RenderedPage[] { | ||
const template = buildTemplate(templates); | ||
const rendered: RenderedPage[] = []; | ||
const renderPage = buildRenderer(templates); | ||
const renderedPages: RenderedPage[] = []; | ||
for (const page of site.pages) { | ||
let contents = template(page, { data: { site } }); | ||
let contents = renderPage(page, { data: { site } }); | ||
if (collapseNewlines) { | ||
contents = contents.replace(/\n{3,}/g, '\n\n'); | ||
} | ||
rendered.push({ | ||
renderedPages.push({ | ||
id: page.id, | ||
@@ -38,3 +33,3 @@ contents, | ||
} | ||
return rendered; | ||
return renderedPages; | ||
} | ||
@@ -55,7 +50,16 @@ | ||
function buildTemplate(templates: Templates): (page: Page, options: TemplateOptions) => string { | ||
function buildRenderer(templates: Templates): (page: Page, options: TemplateOptions) => string { | ||
const pageTemplate = templates.partials?.page; | ||
if (pageTemplate === undefined) { | ||
throw new Error(`Missing 'page' template`); | ||
} | ||
const H = Handlebars.create(); | ||
for (const [name, body] of Object.entries(templates.partials ?? {})) { | ||
H.registerPartial(name, H.compile(body)); | ||
for (const [name, getBody] of Object.entries(templates.partials ?? {})) { | ||
let partial: HandlebarsTemplateDelegate | undefined; | ||
H.registerPartial(name, (...args) => { | ||
partial ??= H.compile(getBody()); | ||
return partial(...args); | ||
}); | ||
} | ||
@@ -69,3 +73,3 @@ | ||
return H.compile(templates.page); | ||
return H.compile('{{>page}}'); | ||
} |
@@ -12,4 +12,11 @@ import { ContractDefinition, SourceUnit } from 'solidity-ast'; | ||
export type PageAssigner = NonNullable<Config['pages']>; | ||
export type PageStructure = NonNullable<Config['pages']>; | ||
export type PageAssigner = Exclude<PageStructure, string>; | ||
const pageAssigner: Record<PageStructure & string, PageAssigner> = { | ||
single: () => 'index.md', | ||
items: (item) => item.name, | ||
}; | ||
export interface Site { | ||
@@ -38,3 +45,5 @@ items: DocItemWithContext[]; | ||
export function buildSite(builds: Build[], assign: PageAssigner): Site { | ||
export function buildSite(builds: Build[], pageStructure: PageStructure): Site { | ||
const assign = typeof pageStructure === 'string' ? pageAssigner[pageStructure] : pageStructure; | ||
const seen = new Set<string>(); | ||
@@ -41,0 +50,0 @@ const items: DocItemWithContext[] = []; |
@@ -5,2 +5,3 @@ import { FunctionDefinition } from 'solidity-ast'; | ||
import { DocItemWithContext } from '../site'; | ||
import { arraysEqual } from './arrays-equal'; | ||
import { execAll } from './execall'; | ||
@@ -28,9 +29,12 @@ import { getContractsInScope } from './scope'; | ||
if (!item.__item_context) throw new Error(`Not an item or item is missing context`); | ||
if (!('documentation' in item && item.documentation)) return {}; | ||
let res: NatSpec = {}; | ||
const docString = 'documentation' in item && item.documentation?.text | ||
? cleanUpDocstring(item.documentation.text) | ||
: ''; | ||
const tagMatches = execAll( | ||
/^(?:@(\w+|custom:[a-z][a-z-]*) )?((?:(?!^@(?:\w+|custom:[a-z][a-z-]*) )[^])*)/m, | ||
cleanUpDocstring(item.documentation.text), | ||
docString, | ||
); | ||
@@ -99,6 +103,16 @@ | ||
} | ||
inheritFrom = [...findAll('FunctionDefinition', parentContract)].find(f => f.name === item.name); | ||
inheritFrom = [...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.__item_context.build.deref('FunctionDefinition', item.baseFunctions[0]!); | ||
const shouldInherit = item.nodeType === 'VariableDeclaration' || arraysEqual(item.parameters.parameters, baseFn.parameters.parameters, p => p.name); | ||
if (shouldInherit) { | ||
inheritFrom = baseFn; | ||
} | ||
} | ||
} | ||
if (res.dev) res.dev = res.dev.trim(); | ||
@@ -110,3 +124,2 @@ if (res.notice) res.notice = res.notice.trim(); | ||
} | ||
// [TODO] inheritance by default as specified by solidity | ||
@@ -113,0 +126,0 @@ return res; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
117949
134
1956
7