diff --git a/Makefile b/Makefile index bccbac90bca748..f4276ab2f4bc4e 100644 --- a/Makefile +++ b/Makefile @@ -871,6 +871,16 @@ out/doc/api/all.json: $(apidocs_json) tools/doc/alljson.mjs | out/doc/api $(call available-node, tools/doc/alljson.mjs) \ fi +# Ensure the documentation landing page mirrors About this documentation. +out/doc/api/documentation.html out/doc/api/documentation.json: \ + doc/api/index.md + +out/doc/api/index.html: out/doc/api/documentation.html doc/api/index.md + cp $< $@ + +out/doc/api/index.json: out/doc/api/documentation.json doc/api/index.md + cp $< $@ + .PHONY: out/doc/api/stability out/doc/api/stability: out/doc/api/all.json tools/doc/stability.mjs | out/doc/api @if [ "$(shell $(node_use_icu))" != "true" ]; then \ diff --git a/tools/doc/allhtml.mjs b/tools/doc/allhtml.mjs index ccf3a10ea7f95b..2d1cd89b9e4147 100644 --- a/tools/doc/allhtml.mjs +++ b/tools/doc/allhtml.mjs @@ -22,7 +22,9 @@ let apicontent = ''; const seen = new Set(['all.html', 'index.html']); for (const link of toc.match(//g)) { - const href = /href="(.*?)"/.exec(link)[1]; + const hrefMatch = /href="(.*?)"/.exec(link); + if (!hrefMatch) continue; + const href = hrefMatch[1]; if (!htmlFiles.includes(href) || seen.has(href)) continue; const data = fs.readFileSync(new URL(`./${href}`, source), 'utf8'); @@ -69,6 +71,11 @@ for (const link of toc.match(//g)) { seen.add(href); } +const aggregatedToc = + '\n'; + // Replace various mentions of index with all. let all = toc.replace(/index\.html/g, 'all.html') .replace('', '') @@ -81,12 +88,28 @@ let all = toc.replace(/index\.html/g, 'all.html') all = all.replace(/.*?\| /, '<title>'); // Insert the combined table of contents. -const tocStart = /<!-- TOC -->/.exec(all); -all = all.slice(0, tocStart.index + tocStart[0].length) + - '<details id="toc" open><summary>Table of contents</summary>\n' + - '<ul>\n' + contents + '</ul>\n' + - '</details>\n' + - all.slice(tocStart.index + tocStart[0].length); +const tocPlaceholder = '<!-- TOC -->'; +if (all.includes(tocPlaceholder)) { + all = all.replace( + tocPlaceholder, + `${tocPlaceholder}\n${aggregatedToc}`, + ); +} else { + const tocElement = /<details[^>]+id="toc"[^>]*>[\s\S]*?<\/details>/; + if (!tocElement.test(all)) { + throw new Error('Failed to locate a TOC container in index.html'); + } + all = all.replace(tocElement, aggregatedToc); +} + +const tocPickerRegex = + /<div class="toc"><ul id="toc-picker">[\s\S]*?<\/ul>\s*<\/div>/; +if (tocPickerRegex.test(all)) { + all = all.replace( + tocPickerRegex, + `<div class="toc"><ul id="toc-picker">\n${contents}</ul></div>`, + ); +} // Replace apicontent with the concatenated set of apicontents from each source. const apiStart = /<\w+ role="main" id="apicontent">\s*/.exec(all); diff --git a/tools/doc/alljson.mjs b/tools/doc/alljson.mjs index a5ff7c7e9badaf..02b87bac2e6681 100644 --- a/tools/doc/alljson.mjs +++ b/tools/doc/alljson.mjs @@ -28,7 +28,9 @@ const seen = new Set(['all.json', 'index.json']); // Extract (and concatenate) the selected data from each document. // Expand hrefs found in json to include source HTML file. for (const link of toc.match(/<a.*?>/g)) { - const href = /href="(.*?)"/.exec(link)[1]; + const hrefMatch = /href="(.*?)"/.exec(link); + if (!hrefMatch) continue; + const href = hrefMatch[1]; const json = href.replace('.html', '.json'); if (!jsonFiles.includes(json) || seen.has(json)) continue; const data = JSON.parse(