solidity-docgen
Version:
Documentation generator for Solidity smart contracts.
64 lines (53 loc) • 1.98 kB
text/typescript
import { ContractDefinition, SourceUnit } from "solidity-ast";
import { findAll, isNodeType } from "solidity-ast/utils";
import { DocItemWithContext } from "../site";
import { filterValues, mapValues } from './map-values';
import { mapKeys } from './map-keys';
type Definition = SourceUnit['nodes'][number] & { name: string };
type Scope = { [name in string]: () => { namespace: Scope } | { definition: Definition } };
export function getContractsInScope(item: DocItemWithContext) {
const cache = new WeakMap<SourceUnit, Scope>();
return filterValues(
flattenScope(run(item.__item_context.file)),
isNodeType('ContractDefinition'),
);
function run(file: SourceUnit): Scope {
if (cache.has(file)) {
return cache.get(file)!;
}
const scope: Scope = {};
cache.set(file, scope);
for (const c of file.nodes) {
if ('name' in c) {
scope[c.name] = () => ({ definition: c });
}
}
for (const i of findAll('ImportDirective', file)) {
const importedFile = item.__item_context.build.deref('SourceUnit', i.sourceUnit);
const importedScope = run(importedFile);
if (i.unitAlias) {
scope[i.unitAlias] = () => ({ namespace: importedScope });
} else if (i.symbolAliases.length === 0) {
Object.assign(scope, importedScope);
} else {
for (const a of i.symbolAliases) {
// Delayed function call supports circular dependencies
scope[a.local ?? a.foreign.name] = importedScope[a.foreign.name] ?? (() => importedScope[a.foreign.name]!());
}
}
};
return scope;
}
}
function flattenScope(scope: Scope): Record<string, Definition> {
return Object.fromEntries(
Object.entries(scope).flatMap(([k, fn]) => {
const v = fn();
if ('definition' in v) {
return [[k, v.definition] as const];
} else {
return Object.entries(mapKeys(flattenScope(v.namespace), k2 => k + '.' + k2));
}
}),
);
}