Skip to content

Commit 4355e9c

Browse files
authored
modules (#36)
1 parent f898be3 commit 4355e9c

4 files changed

Lines changed: 92 additions & 41 deletions

File tree

typedoc.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
/** @type {import('typedoc').TypeDocOptions} */
22
const config = {
33
entryPoints: [
4-
'src/index.ts',
54
'src/protocol/public.ts',
65
'src/threshold/public.ts',
76
'src/proofs/public.ts',
87
'src/dkg/public.ts',
98
'src/vss/public.ts',
109
'src/elgamal/public.ts',
1110
'src/core/public.ts',
11+
'src/index.ts',
1212
],
1313
entryPointStrategy: 'resolve',
1414
alwaysCreateEntryPointModule: true,
1515
plugin: ['typedoc-plugin-markdown'],
1616
out: 'docs/src/content/docs/api/reference',
17-
router: 'member',
17+
router: 'module',
1818
readme: 'typedoc/generated-reference-intro.md',
1919
entryFileName: 'index.md',
2020
navigationJson: 'docs/src/content/docs/api/reference/navigation.json',

typedoc/api-docs-config.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,39 @@ export const apiReferenceRoot = `${docsContentRoot}/api/reference`;
33
export const apiNavigationJson = `${apiReferenceRoot}/navigation.json`;
44

55
export const publicApiDocs: readonly {
6-
apiIndexPage: string;
6+
apiPagePath: string;
77
moduleName: string;
88
}[] = [
99
{
10-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/index.md`,
10+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal.md`,
1111
moduleName: 'threshold-elgamal',
1212
},
1313
{
14-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/protocol/index.md`,
14+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/protocol.md`,
1515
moduleName: 'threshold-elgamal/protocol',
1616
},
1717
{
18-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/threshold/index.md`,
18+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/threshold.md`,
1919
moduleName: 'threshold-elgamal/threshold',
2020
},
2121
{
22-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/proofs/index.md`,
22+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/proofs.md`,
2323
moduleName: 'threshold-elgamal/proofs',
2424
},
2525
{
26-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/dkg/index.md`,
26+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/dkg.md`,
2727
moduleName: 'threshold-elgamal/dkg',
2828
},
2929
{
30-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/vss/index.md`,
30+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/vss.md`,
3131
moduleName: 'threshold-elgamal/vss',
3232
},
3333
{
34-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/elgamal/index.md`,
34+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/elgamal.md`,
3535
moduleName: 'threshold-elgamal/elgamal',
3636
},
3737
{
38-
apiIndexPage: `${apiReferenceRoot}/threshold-elgamal/core/index.md`,
38+
apiPagePath: `${apiReferenceRoot}/threshold-elgamal/core.md`,
3939
moduleName: 'threshold-elgamal/core',
4040
},
4141
] as const;

typedoc/postprocess-site-docs.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ const moduleOrder = new Map(
2222
);
2323

2424
const internalLinkPattern = /(!?\[[^\]]*])\(([^)#\s]+)(#[^)]+)?\)/g;
25-
const breadcrumbPattern = /^\*\*.+?\*\*\r?\n\r?\n\*\*\*\r?\n\r?\n/;
26-
const leadingHeadingPattern = /^# .+\r?\n\r?\n/;
25+
const generatedPreamblePattern = /^[\s\S]*?\r?\n# .+\r?\n\r?\n/;
2726
const sentenceCaseReplacements: readonly (readonly [RegExp, string])[] = [
2827
[/\bType Aliases\b/g, 'Type aliases'],
2928
[/\bType Alias\b/g, 'Type alias'],
@@ -43,9 +42,19 @@ const sentenceCaseReplacements: readonly (readonly [RegExp, string])[] = [
4342
[/\bExtended By\b/g, 'Extended by'],
4443
] as const;
4544

45+
const toReferenceConfigRelativePath = (configPath: string): string =>
46+
path.relative(apiReferenceRoot, configPath).replace(/\\/g, '/');
47+
4648
const toReferenceRelativePath = (absolutePath: string): string =>
4749
path.relative(referenceRoot, absolutePath).replace(/\\/g, '/');
4850

51+
const moduleNameByReferencePath = new Map(
52+
publicApiDocs.map((entry) => [
53+
toReferenceConfigRelativePath(entry.apiPagePath),
54+
entry.moduleName,
55+
]),
56+
);
57+
4958
const collectMarkdownFiles = async (directory: string): Promise<string[]> => {
5059
const files: string[] = [];
5160
const pending = [directory];
@@ -75,20 +84,21 @@ const deriveTitleFromRelativePath = (relativePath: string): string => {
7584
return 'Generated reference';
7685
}
7786

78-
if (relativePath.endsWith('/index.md')) {
79-
const segments = relativePath.slice(0, -'/index.md'.length).split('/');
87+
const moduleName = moduleNameByReferencePath.get(relativePath);
88+
if (moduleName !== undefined) {
89+
const segments = moduleName.split('/');
8090
return segments[segments.length - 1];
8191
}
8292

8393
return path.basename(relativePath, '.md');
8494
};
8595

8696
const deriveSidebarOrder = (relativePath: string): number | undefined => {
87-
if (!relativePath.endsWith('/index.md')) {
97+
const moduleName = moduleNameByReferencePath.get(relativePath);
98+
if (moduleName === undefined) {
8899
return undefined;
89100
}
90101

91-
const moduleName = relativePath.slice(0, -'/index.md'.length);
92102
return moduleOrder.get(moduleName);
93103
};
94104

@@ -143,6 +153,8 @@ const normalizeNavigationTitles = (
143153
}));
144154

145155
const main = async (): Promise<void> => {
156+
await fs.rm(path.join(referenceRoot, 'modules.md'), { force: true });
157+
146158
const navigation = normalizeNavigationTitles(
147159
JSON.parse(
148160
await fs.readFile(navigationPath, 'utf8'),
@@ -173,9 +185,6 @@ const main = async (): Promise<void> => {
173185
visitNavigation(navigation);
174186

175187
const markdownFiles = await collectMarkdownFiles(referenceRoot);
176-
const publicModules = new Set(
177-
publicApiDocs.map((entry) => entry.moduleName),
178-
);
179188

180189
for (const file of markdownFiles) {
181190
const relativePath = toReferenceRelativePath(file);
@@ -184,17 +193,14 @@ const main = async (): Promise<void> => {
184193
deriveTitleFromRelativePath(relativePath);
185194
const order = deriveSidebarOrder(relativePath);
186195
const isGeneratedRoot = relativePath === 'index.md';
187-
const moduleName = relativePath.endsWith('/index.md')
188-
? relativePath.slice(0, -'/index.md'.length)
189-
: undefined;
196+
const moduleName = moduleNameByReferencePath.get(relativePath);
190197
const generatedModuleSummary =
191-
moduleName !== undefined && publicModules.has(moduleName)
198+
moduleName !== undefined
192199
? `Generated reference page for the \`${moduleName}\` export surface.`
193200
: undefined;
194201

195202
let content = await fs.readFile(file, 'utf8');
196-
content = content.replace(breadcrumbPattern, '');
197-
content = content.replace(leadingHeadingPattern, '');
203+
content = content.replace(generatedPreamblePattern, '');
198204
content = rewriteMarkdownLinks(content);
199205
content = rewriteSentenceCase(content);
200206

typedoc/verify-docs.ts

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,34 @@ import config from '../typedoc.config.mjs';
1212

1313
import {
1414
apiNavigationJson,
15+
apiReferenceRoot,
1516
docsContentRoot,
1617
publicApiDocs,
1718
} from './api-docs-config';
1819

1920
const repoRoot = process.cwd();
2021
const docsRoot = path.resolve(repoRoot, docsContentRoot);
2122
const publicRoot = path.resolve(repoRoot, 'docs/public');
23+
const referenceRoot = path.resolve(repoRoot, apiReferenceRoot);
2224
const markdownRoots = ['README.md', docsContentRoot];
25+
const expectedGeneratedApiPagePaths = new Set([
26+
'index.md',
27+
...publicApiDocs.map((entry) =>
28+
path.relative(apiReferenceRoot, entry.apiPagePath).replace(/\\/g, '/'),
29+
),
30+
]);
31+
const expectedGeneratedApiNavigationPaths = new Map(
32+
publicApiDocs.map((entry) => [
33+
path.relative(apiReferenceRoot, entry.apiPagePath).replace(/\\/g, '/'),
34+
entry.moduleName,
35+
]),
36+
);
2337
const requiredApiEntryPages = [
2438
`${docsContentRoot}/api/index.mdx`,
2539
`${docsContentRoot}/api/root-package.mdx`,
26-
...publicApiDocs.map((entry) => entry.apiIndexPage),
40+
...publicApiDocs.map((entry) => entry.apiPagePath),
2741
apiNavigationJson,
2842
] as const;
29-
const requiredApiModules = new Set(
30-
publicApiDocs.map((entry) => entry.moduleName),
31-
);
3243

3344
const markdownLinkPattern = /!?\[[^\]]*]\(([^)]+)\)/g;
3445
const linkTargetPattern = /^([^\s]+)(?:\s+["'][^"']*["'])?$/;
@@ -82,6 +93,8 @@ const resolveLinkCandidates = (
8293
const candidates = new Set<string>([absoluteTarget]);
8394

8495
if (normalizedTarget.endsWith('/')) {
96+
candidates.add(`${absoluteTarget}.md`);
97+
candidates.add(`${absoluteTarget}.mdx`);
8598
candidates.add(path.join(absoluteTarget, 'index.md'));
8699
candidates.add(path.join(absoluteTarget, 'index.mdx'));
87100
candidates.add(path.join(absoluteTarget, 'README.md'));
@@ -210,6 +223,30 @@ const verifyBaseAwareLinks = async (): Promise<string[]> => {
210223
return failures;
211224
};
212225

226+
const verifyGeneratedApiLayout = async (): Promise<string[]> => {
227+
const failures: string[] = [];
228+
const generatedMarkdownFiles = await collectMarkdownFiles(apiReferenceRoot);
229+
const seenGeneratedApiPagePaths = new Set(
230+
generatedMarkdownFiles.map((file) =>
231+
path.relative(referenceRoot, file).replace(/\\/g, '/'),
232+
),
233+
);
234+
235+
for (const expectedPagePath of expectedGeneratedApiPagePaths) {
236+
if (!seenGeneratedApiPagePaths.has(expectedPagePath)) {
237+
failures.push(`missing generated page "${expectedPagePath}"`);
238+
}
239+
}
240+
241+
for (const seenPagePath of seenGeneratedApiPagePaths) {
242+
if (!expectedGeneratedApiPagePaths.has(seenPagePath)) {
243+
failures.push(`unexpected generated page "${seenPagePath}"`);
244+
}
245+
}
246+
247+
return failures;
248+
};
249+
213250
const verifyApiEntryPages = async (): Promise<string[]> => {
214251
const failures: string[] = [];
215252

@@ -232,7 +269,7 @@ const verifyApiEntryPages = async (): Promise<string[]> => {
232269
title?: string;
233270
path?: string;
234271
}[];
235-
const seenModules = new Set<string>();
272+
const seenNavigationPaths = new Set<string>();
236273

237274
const visitNavigationItems = (
238275
items: readonly {
@@ -242,11 +279,8 @@ const verifyApiEntryPages = async (): Promise<string[]> => {
242279
}[],
243280
): void => {
244281
for (const item of items) {
245-
if (
246-
typeof item.path === 'string' &&
247-
item.path.endsWith('/index.md')
248-
) {
249-
seenModules.add(item.path.slice(0, -'/index.md'.length));
282+
if (typeof item.path === 'string') {
283+
seenNavigationPaths.add(item.path);
250284
}
251285

252286
if (Array.isArray(item.children)) {
@@ -263,16 +297,19 @@ const verifyApiEntryPages = async (): Promise<string[]> => {
263297

264298
visitNavigationItems(navigationJson);
265299

266-
for (const moduleName of requiredApiModules) {
267-
if (!seenModules.has(moduleName)) {
300+
for (const [
301+
navigationPathValue,
302+
moduleName,
303+
] of expectedGeneratedApiNavigationPaths) {
304+
if (!seenNavigationPaths.has(navigationPathValue)) {
268305
failures.push(`navigation.json missing module "${moduleName}"`);
269306
}
270307
}
271308

272-
for (const moduleName of seenModules) {
273-
if (moduleName !== undefined && !requiredApiModules.has(moduleName)) {
309+
for (const seenNavigationPath of seenNavigationPaths) {
310+
if (!expectedGeneratedApiNavigationPaths.has(seenNavigationPath)) {
274311
failures.push(
275-
`navigation.json contains non-exported module "${moduleName}"`,
312+
`navigation.json contains unexpected path "${seenNavigationPath}"`,
276313
);
277314
}
278315
}
@@ -343,6 +380,7 @@ const verifyTypeDocSummaries = async (): Promise<string[]> => {
343380
const main = async (): Promise<void> => {
344381
const linkFailures = await verifyLinks();
345382
const baseAwareFailures = await verifyBaseAwareLinks();
383+
const generatedLayoutFailures = await verifyGeneratedApiLayout();
346384
const apiFailures = await verifyApiEntryPages();
347385
const summaryFailures = await verifyTypeDocSummaries();
348386

@@ -358,6 +396,13 @@ const main = async (): Promise<void> => {
358396
failures.push(...baseAwareFailures.map((failure) => `- ${failure}`));
359397
}
360398

399+
if (generatedLayoutFailures.length > 0) {
400+
failures.push('Generated API layout violations:');
401+
failures.push(
402+
...generatedLayoutFailures.map((failure) => `- ${failure}`),
403+
);
404+
}
405+
361406
if (apiFailures.length > 0) {
362407
failures.push('Missing generated API entry pages:');
363408
failures.push(...apiFailures.map((failure) => `- ${failure}`));

0 commit comments

Comments
 (0)