@microsoft/api-documenter
Version:
Read JSON files from api-extractor, generate documentation pages
194 lines • 7.65 kB
JavaScript
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.MarkdownEmitter = void 0;
const tsdoc_1 = require("@microsoft/tsdoc");
const node_core_library_1 = require("@rushstack/node-core-library");
const IndentedWriter_1 = require("../utils/IndentedWriter");
/**
* Renders MarkupElement content in the Markdown file format.
* For more info: https://en.wikipedia.org/wiki/Markdown
*/
class MarkdownEmitter {
emit(stringBuilder, docNode, options) {
const writer = new IndentedWriter_1.IndentedWriter(stringBuilder);
const context = {
writer,
boldRequested: false,
italicRequested: false,
writingBold: false,
writingItalic: false,
options
};
this.writeNode(docNode, context, false);
writer.ensureNewLine(); // finish the last line
return writer.toString();
}
getEscapedText(text) {
const textWithBackslashes = text
.replace(/\\/g, '\\\\') // first replace the escape character
.replace(/[*#[\]_|`~]/g, (x) => '\\' + x) // then escape any special characters
.replace(/---/g, '\\-\\-\\-') // hyphens only if it's 3 or more
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
return textWithBackslashes;
}
getTableEscapedText(text) {
return text
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\|/g, '|');
}
/**
* @virtual
*/
writeNode(docNode, context, docNodeSiblings) {
const writer = context.writer;
switch (docNode.kind) {
case tsdoc_1.DocNodeKind.PlainText: {
const docPlainText = docNode;
this.writePlainText(docPlainText.text, context);
break;
}
case tsdoc_1.DocNodeKind.HtmlStartTag:
case tsdoc_1.DocNodeKind.HtmlEndTag: {
const docHtmlTag = docNode;
// write the HTML element verbatim into the output
writer.write(docHtmlTag.emitAsHtml());
break;
}
case tsdoc_1.DocNodeKind.CodeSpan: {
const docCodeSpan = docNode;
writer.write('`');
writer.write(docCodeSpan.code);
writer.write('`');
break;
}
case tsdoc_1.DocNodeKind.LinkTag: {
const docLinkTag = docNode;
if (docLinkTag.codeDestination) {
this.writeLinkTagWithCodeDestination(docLinkTag, context);
}
else if (docLinkTag.urlDestination) {
this.writeLinkTagWithUrlDestination(docLinkTag, context);
}
else if (docLinkTag.linkText) {
this.writePlainText(docLinkTag.linkText, context);
}
break;
}
case tsdoc_1.DocNodeKind.Paragraph: {
const docParagraph = docNode;
const trimmedParagraph = tsdoc_1.DocNodeTransforms.trimSpacesInParagraph(docParagraph);
this.writeNodes(trimmedParagraph.nodes, context);
writer.ensureNewLine();
writer.writeLine();
break;
}
case tsdoc_1.DocNodeKind.FencedCode: {
const docFencedCode = docNode;
writer.ensureNewLine();
writer.write('```');
writer.write(docFencedCode.language);
writer.writeLine();
writer.write(docFencedCode.code);
writer.ensureNewLine();
writer.writeLine('```');
break;
}
case tsdoc_1.DocNodeKind.Section: {
const docSection = docNode;
this.writeNodes(docSection.nodes, context);
break;
}
case tsdoc_1.DocNodeKind.SoftBreak: {
if (!/^\s?$/.test(writer.peekLastCharacter())) {
writer.write(' ');
}
break;
}
case tsdoc_1.DocNodeKind.EscapedText: {
const docEscapedText = docNode;
this.writePlainText(docEscapedText.decodedText, context);
break;
}
case tsdoc_1.DocNodeKind.ErrorText: {
const docErrorText = docNode;
this.writePlainText(docErrorText.text, context);
break;
}
case tsdoc_1.DocNodeKind.InlineTag: {
break;
}
case tsdoc_1.DocNodeKind.BlockTag: {
const tagNode = docNode;
console.warn('Unsupported block tag: ' + tagNode.tagName);
break;
}
default:
throw new node_core_library_1.InternalError('Unsupported DocNodeKind kind: ' + docNode.kind);
}
}
/** @virtual */
writeLinkTagWithCodeDestination(docLinkTag, context) {
// The subclass needs to implement this to support code destinations
throw new node_core_library_1.InternalError('writeLinkTagWithCodeDestination()');
}
/** @virtual */
writeLinkTagWithUrlDestination(docLinkTag, context) {
const linkText = docLinkTag.linkText !== undefined ? docLinkTag.linkText : docLinkTag.urlDestination;
const encodedLinkText = this.getEscapedText(linkText.replace(/\s+/g, ' '));
context.writer.write('[');
context.writer.write(encodedLinkText);
context.writer.write(`](${docLinkTag.urlDestination})`);
}
writePlainText(text, context) {
const writer = context.writer;
// split out the [ leading whitespace, content, trailing whitespace ]
const parts = text.match(/^(\s*)(.*?)(\s*)$/) || [];
writer.write(parts[1]); // write leading whitespace
const middle = parts[2];
if (middle !== '') {
switch (writer.peekLastCharacter()) {
case '':
case '\n':
case ' ':
case '[':
case '>':
// okay to put a symbol
break;
default:
// This is no problem: "**one** *two* **three**"
// But this is trouble: "**one***two***three**"
// The most general solution: "**one**<!-- -->*two*<!-- -->**three**"
writer.write('<!-- -->');
break;
}
if (context.boldRequested) {
writer.write('**');
}
if (context.italicRequested) {
writer.write('_');
}
writer.write(this.getEscapedText(middle));
if (context.italicRequested) {
writer.write('_');
}
if (context.boldRequested) {
writer.write('**');
}
}
writer.write(parts[3]); // write trailing whitespace
}
writeNodes(docNodes, context) {
for (const docNode of docNodes) {
this.writeNode(docNode, context, docNodes.length > 1);
}
}
}
exports.MarkdownEmitter = MarkdownEmitter;
//# sourceMappingURL=MarkdownEmitter.js.map
;