UNPKG

@microsoft/api-extractor

Version:

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

216 lines (214 loc) 8.34 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 }); const PrettyPrinter_1 = require("./PrettyPrinter"); class TypeScriptHelpers { /** * Returns the Symbol for the provided Declaration. This is a workaround for a missing * feature of the TypeScript Compiler API. It is the only apparent way to reach * certain data structures, and seems to always work, but is not officially documented. * * @returns The associated Symbol. If there is no semantic information (e.g. if the * declaration is an extra semicolon somewhere), then "undefined" is returned. */ static tryGetSymbolForDeclaration(declaration) { /* tslint:disable:no-any */ const symbol = declaration.symbol; /* tslint:enable:no-any */ return symbol; } /** * Same semantics as tryGetSymbolForDeclaration(), but throws an exception if the symbol * cannot be found. */ static getSymbolForDeclaration(declaration) { const symbol = TypeScriptHelpers.tryGetSymbolForDeclaration(declaration); if (!symbol) { PrettyPrinter_1.default.throwUnexpectedSyntaxError(declaration, 'Unable to determine the semantic information for this declaration'); } return symbol; } /** * Returns the JSDoc comments associated with the specified node, if any. * * Example: * "This \n is \n a comment" from "\/** This\r\n* is\r\n* a comment *\/ */ static getJsdocComments(node, errorLogger) { let jsdoc = ''; // tslint:disable-next-line:no-any const nodeJsdocObjects = node.jsDoc; if (nodeJsdocObjects && nodeJsdocObjects.length > 0) { // Use the JSDoc closest to the declaration const lastJsdocIndex = nodeJsdocObjects.length - 1; const jsdocFullText = nodeJsdocObjects[lastJsdocIndex].getText(); const jsdocLines = jsdocFullText.split(TypeScriptHelpers.newLineRegEx); const jsdocStartSeqExists = TypeScriptHelpers.jsdocStartRegEx.test(jsdocLines[0].toString()); // Report error for each missing sequence separately if (!jsdocStartSeqExists) { errorLogger('Jsdoc comment must begin with a \"/**\" sequence.'); return ''; } const jsdocEndSeqExists = TypeScriptHelpers.jsdocEndRegEx.test(jsdocLines[jsdocLines.length - 1].toString()); if (!jsdocEndSeqExists) { errorLogger('Jsdoc comment must end with a \"*/\" sequence.'); return ''; } jsdoc = TypeScriptHelpers.removeJsdocSequences(jsdocLines); } return jsdoc; } /** * Helper function to remove the comment stars ('/**'. '*', '/*) from lines of comment text. * * Example: * ["\/**", "*This \n", "*is \n", "*a comment", "*\/"] to "This \n is \n a comment" */ static removeJsdocSequences(textLines) { // Remove '/**' textLines[0] = textLines[0].replace(TypeScriptHelpers.jsdocStartRegEx, ''); if (textLines[0] === '') { textLines.shift(); } // Remove '*/' textLines[textLines.length - 1] = textLines[textLines.length - 1].replace(TypeScriptHelpers.jsdocEndRegEx, ''); if (textLines[textLines.length - 1] === '') { textLines.pop(); } // Remove the leading '*' from any intermediate lines if (textLines.length > 0) { for (let i = 0; i < textLines.length; i++) { textLines[i] = textLines[i].replace(TypeScriptHelpers.jsdocIntermediateRegEx, ''); } } return textLines.join('\n'); } /** * Similar to calling string.split() with a RegExp, except that the delimiters * are included in the result. * * Example: _splitStringWithRegEx("ABCDaFG", /A/gi) -> [ "A", "BCD", "a", "FG" ] * Example: _splitStringWithRegEx("", /A/gi) -> [ ] * Example: _splitStringWithRegEx("", /A?/gi) -> [ "" ] */ static splitStringWithRegEx(text, regExp) { if (!regExp.global) { throw new Error('RegExp must have the /g flag'); } if (text === undefined) { return []; } const result = []; let index = 0; let match; do { match = regExp.exec(text); if (match) { if (match.index > index) { result.push(text.substring(index, match.index)); } const matchText = match[0]; if (!matchText) { // It might be interesting to support matching e.g. '\b', but regExp.exec() // doesn't seem to iterate properly in this situation. throw new Error('The regular expression must match a nonzero number of characters'); } result.push(matchText); index = regExp.lastIndex; } } while (match && regExp.global); if (index < text.length) { result.push(text.substr(index)); } return result; } /** * Extracts the body of a TypeScript comment and returns it. */ // Examples: // "/**\n * this is\n * a test\n */\n" --> "this is\na test" // "/** single line comment */" --> "single line comment" static extractCommentContent(text) { const lines = text.replace('\r', '').split('\n'); let State; (function (State) { State[State["Start"] = 0] = "Start"; State[State["Body"] = 1] = "Body"; State[State["Done"] = 2] = "Done"; State[State["Error"] = 3] = "Error"; })(State || (State = {})); let state = State.Start; const startRegExp = /^\s*\/\*\*+ ?/; const bodyRegExp = /^\s*\* ?/; const endRegExp = /^\s*\*+\/\s*$/; const singleLineEndRegExp = / ?\*+\/\s*$/; let content = ''; for (const line of lines) { if (line.trim().length === 0) { continue; } let modified = line; switch (state) { case State.Start: if (line.match(startRegExp)) { modified = line.replace(startRegExp, ''); if (modified.match(singleLineEndRegExp)) { modified = modified.replace(singleLineEndRegExp, ''); state = State.Done; } else { state = State.Body; } } else { state = State.Error; } break; case State.Body: if (line.match(endRegExp)) { modified = line.replace(endRegExp, ''); state = State.Done; } else if (line.match(bodyRegExp)) { modified = line.replace(bodyRegExp, ''); } else { state = State.Error; } break; case State.Done: state = State.Error; break; } if (modified !== '') { if (content !== '') { content += '\n'; } content += modified; } } if (state !== State.Done) { return '[ERROR PARSING COMMENT]'; } return content; } } /** * Splits by the characters '\r\n'. */ TypeScriptHelpers.newLineRegEx = /\r\n|\n/g; /** * Start sequence is '/**'. */ TypeScriptHelpers.jsdocStartRegEx = /^\s*\/\*\*\s?/g; /** * End sequence is '*\/'. */ TypeScriptHelpers.jsdocEndRegEx = /\s*\*\/\s*$/g; /** * Intermediate lines of JSDoc comment character. */ TypeScriptHelpers.jsdocIntermediateRegEx = /^\s*[*]\s?/g; exports.default = TypeScriptHelpers; //# sourceMappingURL=TypeScriptHelpers.js.map