api-extractor: document doc generation a bit

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-09-11 19:21:50 +02:00
parent e79b23d38b
commit 7da91310b0
+36 -8
View File
@@ -301,6 +301,14 @@ async function runApiExtraction({
}
}
/*
WARNING: Bring a blanket if you're gonna read the code below
There's some weird shit going on here, and it's because we cba
forking rushstash to modify the api-documenter markdown generation,
which otherwise is the recommended way to do customizations.
*/
async function buildDocs({
inputDir,
outputDir,
@@ -308,6 +316,8 @@ async function buildDocs({
inputDir: string;
outputDir: string;
}) {
// We start by constructing our own model from the files so that
// we get a change to modify them, as the model is otherwise read-only.
const parseFile = async (filename: string): Promise<any> => {
console.log(`Reading ${filename}`);
return fs.readJson(resolvePath(inputDir, filename));
@@ -331,9 +341,9 @@ async function buildDocs({
newModel.addMember(pkg);
}
await fs.remove(outputDir);
await fs.ensureDir(outputDir);
// This is root of the documentation generation, but it's not directly
// responsible for generating markdown, it just constructs an AST that
// is the consumed by an emitter to actually write the files.
const documenter = new MarkdownDocumenter({
apiModel: newModel,
documenterConfig: {
@@ -346,6 +356,12 @@ async function buildDocs({
outputFolder: outputDir,
});
// We're accessing a lot of internal things...
const anyDocumenter = documenter as any;
// The doc AST need to be extended with custom nodes if we want to
// add any extra content.
// This one is for the YAML front matter that we need for the microsite.
class DocFrontMatter extends DocNode {
static kind = 'DocFrontMatter';
@@ -372,8 +388,7 @@ async function buildDocs({
}
}
const anyDocumenter = documenter as any;
// It's a strict model, we gotta register the allowed usage of our new node
anyDocumenter._tsdocConfiguration.docNodeManager.registerDocNodes(
'@backstage/docs',
[{ docNodeKind: DocFrontMatter.kind, constructor: DocFrontMatter }],
@@ -383,6 +398,8 @@ async function buildDocs({
[DocFrontMatter.kind],
);
// This is where we actually write the markdown and where we can hook
// in the rendering of our own nodes.
class CustomCustomMarkdownEmitter extends CustomMarkdownEmitter {
/** @override */
protected writeNode(
@@ -408,17 +425,22 @@ async function buildDocs({
/** @override */
emit(stringBuilder, docNode, options) {
// Hack to get rid of the leading comment
// Hack to get rid of the leading comment of each file, since
// we want the front matter to come first
stringBuilder._chunks.length = 0;
return super.emit(stringBuilder, docNode, options);
}
}
const emitter = new CustomCustomMarkdownEmitter(newModel);
anyDocumenter._markdownEmitter = emitter;
// The emitter is an internal thing, but it's fine to rewrite
anyDocumenter._markdownEmitter = new CustomCustomMarkdownEmitter(newModel);
// Gotta keep this around so we can call the real implementation
const oldWrite = anyDocumenter._writeBreadcrumb;
// We don't really get many chances to modify the generated AST
// so we hook in wherever we can. In this case we add the front matter
// just before writing the breadcrumbs at the top.
anyDocumenter._writeBreadcrumb = function patchedWriteBreadcrumb(
output,
apiItem,
@@ -438,6 +460,7 @@ async function buildDocs({
description = `API Reference for ${apiItem.name}`;
}
// Add our front matter
output.appendNodeInParagraph(
new DocFrontMatter({
configuration: this._tsdocConfiguration,
@@ -447,15 +470,20 @@ async function buildDocs({
}),
);
// Now write the actual breadcrumbs
oldWrite.call(this, output, apiItem);
// We wanna ignore the header that always gets written after the breadcrumb
// This otherwise becomes more or less a duplicate of the title in the front matter
const oldAppendNode = output.appendNode;
output.appendNode = () => {
output.appendNode = oldAppendNode;
};
};
// Clean up existing stuff and write ALL the docs!
await fs.remove(outputDir);
await fs.ensureDir(outputDir);
documenter.generateFiles();
}