UNPKG

@microsoft/api-extractor

Version:

Analyze the exported API for a TypeScript library and generate reviews, documentation, and .d.ts rollups

181 lines 7.4 kB
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. import * as ts from 'typescript'; import { ExcerptTokenKind } from '@microsoft/api-extractor-model'; import { Span } from '../analyzer/Span'; import { condenseTokens } from './condenseTokens'; export class ExcerptBuilder { /** * Appends a blank line to the `excerptTokens` list. * @param excerptTokens - The target token list to append to */ static addBlankLine(excerptTokens) { let newlines = '\n\n'; // If the existing text already ended with a newline, then only append one newline if (excerptTokens.length > 0) { const previousText = excerptTokens[excerptTokens.length - 1].text; if (/\n$/.test(previousText)) { newlines = '\n'; } } excerptTokens.push({ kind: ExcerptTokenKind.Content, text: newlines }); } /** * Appends the signature for the specified `AstDeclaration` to the `excerptTokens` list. * @param excerptTokens - The target token list to append to * @param nodeTransforms - A list of child nodes whose token ranges we want to capture */ static addDeclaration(excerptTokens, astDeclaration, nodeTransforms, referenceGenerator) { let stopBeforeChildKind = undefined; switch (astDeclaration.declaration.kind) { case ts.SyntaxKind.ClassDeclaration: case ts.SyntaxKind.EnumDeclaration: case ts.SyntaxKind.InterfaceDeclaration: // FirstPunctuation = "{" stopBeforeChildKind = ts.SyntaxKind.FirstPunctuation; break; case ts.SyntaxKind.ModuleDeclaration: // ModuleBlock = the "{ ... }" block stopBeforeChildKind = ts.SyntaxKind.ModuleBlock; break; } const span = new Span(astDeclaration.declaration); const transformsByNode = new Map(); const captureTokenRanges = []; for (const nodeTransform of nodeTransforms || []) { transformsByNode.set(nodeTransform.node, nodeTransform); if (nodeTransform.captureTokenRange) { captureTokenRanges.push(nodeTransform.captureTokenRange); } } _buildSpan(excerptTokens, span, { referenceGenerator: referenceGenerator, startingNode: span.node, stopBeforeChildKind, transformsByNode: transformsByNode, lastAppendedTokenIsSeparator: false }); condenseTokens(excerptTokens, captureTokenRanges); } static createEmptyTokenRange() { return { startIndex: 0, endIndex: 0 }; } } /** @returns false if we encountered a token that causes iteration to stop. */ function _buildSpan(excerptTokens, span, state) { if (span.kind === ts.SyntaxKind.JSDocComment) { // Discard any comments return true; } // Can this node start a excerpt? const transform = state.transformsByNode.get(span.node); let captureTokenRange = undefined; if (transform) { captureTokenRange = transform.captureTokenRange; if (transform.replacementText !== undefined) { excerptTokens.push({ kind: ExcerptTokenKind.Content, text: transform.replacementText }); state.lastAppendedTokenIsSeparator = false; if (captureTokenRange) { captureTokenRange.startIndex = excerptTokens.length; captureTokenRange.endIndex = captureTokenRange.startIndex + 1; } return true; } } let excerptStartIndex = 0; if (captureTokenRange) { // We will assign capturedTokenRange.startIndex to be the index of the next token to be appended excerptStartIndex = excerptTokens.length; } if (span.prefix) { let canonicalReference = undefined; if (span.kind === ts.SyntaxKind.Identifier) { const name = span.node; if (!_isDeclarationName(name)) { canonicalReference = state.referenceGenerator.getDeclarationReferenceForIdentifier(name); } } if (canonicalReference) { _appendToken(excerptTokens, ExcerptTokenKind.Reference, span.prefix, canonicalReference); } else { _appendToken(excerptTokens, ExcerptTokenKind.Content, span.prefix); } state.lastAppendedTokenIsSeparator = false; } for (const child of span.children) { if (span.node === state.startingNode) { if (state.stopBeforeChildKind && child.kind === state.stopBeforeChildKind) { // We reached a child whose kind is stopBeforeChildKind, so stop traversing return false; } } if (!_buildSpan(excerptTokens, child, state)) { return false; } } if (span.suffix) { _appendToken(excerptTokens, ExcerptTokenKind.Content, span.suffix); state.lastAppendedTokenIsSeparator = false; } if (span.separator) { _appendToken(excerptTokens, ExcerptTokenKind.Content, span.separator); state.lastAppendedTokenIsSeparator = true; } // Are we building a excerpt? If so, set its range if (captureTokenRange) { captureTokenRange.startIndex = excerptStartIndex; // We will assign capturedTokenRange.startIndex to be the index after the last token // that was appended so far. However, if the last appended token was a separator, omit // it from the range. let excerptEndIndex = excerptTokens.length; if (state.lastAppendedTokenIsSeparator) { excerptEndIndex--; } captureTokenRange.endIndex = excerptEndIndex; } return true; } function _appendToken(excerptTokens, excerptTokenKind, text, canonicalReference) { if (text.length === 0) { return; } const excerptToken = { kind: excerptTokenKind, text: text }; if (canonicalReference !== undefined) { excerptToken.canonicalReference = canonicalReference.toString(); } excerptTokens.push(excerptToken); } function _isDeclarationName(name) { return _isDeclaration(name.parent) && name.parent.name === name; } function _isDeclaration(node) { switch (node.kind) { case ts.SyntaxKind.FunctionDeclaration: case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.VariableDeclaration: case ts.SyntaxKind.Parameter: case ts.SyntaxKind.EnumDeclaration: case ts.SyntaxKind.ClassDeclaration: case ts.SyntaxKind.ClassExpression: case ts.SyntaxKind.ModuleDeclaration: case ts.SyntaxKind.MethodDeclaration: case ts.SyntaxKind.MethodSignature: case ts.SyntaxKind.PropertyDeclaration: case ts.SyntaxKind.PropertySignature: case ts.SyntaxKind.GetAccessor: case ts.SyntaxKind.SetAccessor: case ts.SyntaxKind.InterfaceDeclaration: case ts.SyntaxKind.TypeAliasDeclaration: case ts.SyntaxKind.TypeParameter: case ts.SyntaxKind.EnumMember: case ts.SyntaxKind.BindingElement: return true; default: return false; } } //# sourceMappingURL=ExcerptBuilder.js.map