UNPKG

@microsoft/api-documenter

Version:

Read JSON files from api-extractor, generate documentation pages

194 lines 7.65 kB
"use strict"; // 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, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); return textWithBackslashes; } getTableEscapedText(text) { return text .replace(/&/g, '&amp;') .replace(/"/g, '&quot;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/\|/g, '&#124;'); } /** * @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