UNPKG

@microsoft/api-extractor

Version:

Validate, document, and review the exported API for a TypeScript library

350 lines (348 loc) 12.4 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 }); /** * Provides various operations for working with MarkupElement objects. * * @public */ class Markup { /** * Constructs an IMarkupText element representing the specified text string, with * optional formatting. * * @remarks * NOTE: All whitespace (including newlines) will be collapsed to single spaces. * This behavior is similar to how HTML handles whitespace. To preserve * newlines, use {@link Markup.createTextParagraphs} instead. */ static createTextElements(text, options) { if (!text) { return []; } else { const result = { kind: 'text', text: Markup._trimRawText(text) }; if (options) { if (options.bold) { result.bold = true; } if (options.italics) { result.italics = true; } } // The return value is represented as an array containing at most one element. // Another possible design would be to return a single IMarkupText object that // is possibly undefined; however, in practice appending arrays turns out to be // more concise than checking for undefined. return [result]; } } /** * This function is similar to {@link Markup.createTextElements}, except that multiple newlines * will be converted to a Markup.PARAGRAPH object. */ static createTextParagraphs(text, options) { const result = []; if (text) { // Split up the paragraphs for (const paragraph of text.split(/\n\s*\n/g)) { if (result.length > 0) { result.push(Markup.PARAGRAPH); } result.push(...Markup.createTextElements(paragraph, options)); } } return result; } /** * Constructs an IMarkupApiLink element that represents a hyperlink to the specified * API object. The hyperlink is applied to an existing stream of markup elements. * @param textElements - the markup sequence that will serve as the link text * @param target - the API object that the hyperlink will point to */ static createApiLink(textElements, target) { if (!textElements.length) { throw new Error('Missing text for link'); } if (!target.packageName || target.packageName.length < 1) { throw new Error('The IApiItemReference.packageName cannot be empty'); } return { kind: 'api-link', elements: textElements, target: target }; } /** * Constructs an IMarkupApiLink element that represents a hyperlink to the specified * API object. The hyperlink is applied to a plain text string. * @param text - the text string that will serve as the link text * @param target - the API object that the hyperlink will point to */ static createApiLinkFromText(text, target) { return Markup.createApiLink(Markup.createTextElements(text), target); } /** * Constructs an IMarkupWebLink element that represents a hyperlink an internet URL. * @param textElements - the markup sequence that will serve as the link text * @param targetUrl - the URL that the hyperlink will point to */ static createWebLink(textElements, targetUrl) { if (!textElements.length) { throw new Error('Missing text for link'); } if (!targetUrl || !targetUrl.trim()) { throw new Error('Missing link target'); } return { kind: 'web-link', elements: textElements, targetUrl: targetUrl }; } /** * Constructs an IMarkupWebLink element that represents a hyperlink an internet URL. * @param text - the plain text string that will serve as the link text * @param targetUrl - the URL that the hyperlink will point to */ static createWebLinkFromText(text, targetUrl) { return Markup.createWebLink(Markup.createTextElements(text), targetUrl); } /** * Constructs an IMarkupHighlightedText element representing a program code text * with optional syntax highlighting */ static createCode(code, highlighter) { if (!code) { throw new Error('The code parameter is missing'); } return { kind: 'code', text: code, highlighter: highlighter || 'plain' }; } /** * Constructs an IMarkupHeading1 element with the specified title text */ static createHeading1(text) { return { kind: 'heading1', text: Markup._trimRawText(text) }; } /** * Constructs an IMarkupHeading2 element with the specified title text */ static createHeading2(text) { return { kind: 'heading2', text: Markup._trimRawText(text) }; } /** * Constructs an IMarkupCodeBox element representing a program code text * with the specified syntax highlighting */ static createCodeBox(code, highlighter) { if (!code) { throw new Error('The code parameter is missing'); } return { kind: 'code-box', text: code, highlighter: highlighter }; } /** * Constructs an IMarkupNoteBox element that will display the specified markup content */ static createNoteBox(textElements) { return { kind: 'note-box', elements: textElements }; } /** * Constructs an IMarkupNoteBox element that will display the specified plain text string */ static createNoteBoxFromText(text) { return Markup.createNoteBox(Markup.createTextElements(text)); } /** * Constructs an IMarkupTableRow element containing the specified cells, which each contain a * sequence of MarkupBasicElement content */ static createTableRow(cellValues = undefined) { const row = { kind: 'table-row', cells: [] }; if (cellValues) { for (const cellValue of cellValues) { const cell = { kind: 'table-cell', elements: cellValue }; row.cells.push(cell); } } return row; } /** * Constructs an IMarkupTable element containing the specified header cells, which each contain a * sequence of MarkupBasicElement content. * @remarks * The table initially has zero rows. */ static createTable(headerCellValues = undefined) { let header = undefined; if (headerCellValues) { header = Markup.createTableRow(headerCellValues); } return { kind: 'table', header: header, rows: [] }; } /** * Constructs an IMarkupTable element with the specified title. */ static createPage(title) { return { kind: 'page', breadcrumb: [], title: Markup._trimRawText(title), elements: [] }; } /** * Extracts plain text from the provided markup elements, discarding any formatting. * * @remarks * The returned string is suitable for counting words or extracting search keywords. * Its formatting is not guaranteed, and may change in future updates of this API. * * API Extractor determines whether an API is "undocumented" by using extractTextContent() * to extract the text from its summary, and then counting the number of words. */ static extractTextContent(elements) { // Pass a buffer, since "+=" uses less memory than "+" const buffer = { text: '' }; Markup._extractTextContent(elements, buffer); return buffer.text; } /** * Use this to clean up a MarkupElement sequence, assuming the sequence is now in * its final form. * * @remarks * The following operations are performed: * * 1. Remove leading/trailing white space around paragraphs * * 2. Remove redundant paragraph elements */ static normalize(elements) { let i = 0; while (i < elements.length) { const element = elements[i]; const previousElement = i - 1 >= 0 ? elements[i - 1] : undefined; const nextElement = i + 1 < elements.length ? elements[i + 1] : undefined; const paragraphBefore = !!(previousElement && previousElement.kind === 'paragraph'); const paragraphAfter = !!(nextElement && nextElement.kind === 'paragraph'); if (element.kind === 'paragraph') { if (i === 0 || i === elements.length - 1 || paragraphBefore) { // Delete this element. We do not update i because the "previous" item // is unchanged on the next loop. elements.splice(i, 1); continue; } } else if (element.kind === 'text') { const textElement = element; if (paragraphBefore || i === 0) { textElement.text = textElement.text.replace(/^\s+/, ''); // trim left } if (paragraphAfter || i === elements.length - 1) { textElement.text = textElement.text.replace(/\s+$/, ''); // trim right } } ++i; } } static _extractTextContent(elements, buffer) { for (const element of elements) { switch (element.kind) { case 'api-link': buffer.text += Markup.extractTextContent(element.elements); break; case 'break': buffer.text += '\n'; break; case 'code': case 'code-box': break; case 'heading1': case 'heading2': buffer.text += element.text; break; case 'note-box': buffer.text += Markup.extractTextContent(element.elements); break; case 'page': buffer.text += element.title + '\n'; buffer.text += Markup.extractTextContent(element.elements); break; case 'paragraph': buffer.text += '\n\n'; break; case 'table': if (element.header) { buffer.text += Markup.extractTextContent([element.header]); } buffer.text += Markup.extractTextContent(element.rows); break; case 'table-cell': buffer.text += Markup.extractTextContent(element.elements); buffer.text += '\n'; break; case 'table-row': buffer.text += Markup.extractTextContent(element.cells); buffer.text += '\n'; break; case 'text': buffer.text += element.text; break; case 'web-link': buffer.text += Markup.extractTextContent(element.elements); break; default: throw new Error('Unsupported element kind'); } } } static _trimRawText(text) { // Replace multiple whitespaces with a single space return text.replace(/\s+/g, ' '); } } /** * A predefined constant for the IMarkupLineBreak element. */ Markup.BREAK = { kind: 'break' }; /** * A predefined constant for the IMarkupParagraph element. */ Markup.PARAGRAPH = { kind: 'paragraph' }; exports.Markup = Markup; //# sourceMappingURL=Markup.js.map