in apps/api-extractor/src/generators/ApiReportGenerator.ts [252:427]
private static _modifySpan(
collector: Collector,
span: Span,
entity: CollectorEntity,
astDeclaration: AstDeclaration,
insideTypeLiteral: boolean
): void {
// Should we process this declaration at all?
// eslint-disable-next-line no-bitwise
if ((astDeclaration.modifierFlags & ts.ModifierFlags.Private) !== 0) {
span.modification.skipAll();
return;
}
const previousSpan: Span | undefined = span.previousSibling;
let recurseChildren: boolean = true;
let sortChildren: boolean = false;
switch (span.kind) {
case ts.SyntaxKind.JSDocComment:
span.modification.skipAll();
// For now, we don't transform JSDoc comment nodes at all
recurseChildren = false;
break;
case ts.SyntaxKind.ExportKeyword:
case ts.SyntaxKind.DefaultKeyword:
case ts.SyntaxKind.DeclareKeyword:
// Delete any explicit "export" or "declare" keywords -- we will re-add them below
span.modification.skipAll();
break;
case ts.SyntaxKind.InterfaceKeyword:
case ts.SyntaxKind.ClassKeyword:
case ts.SyntaxKind.EnumKeyword:
case ts.SyntaxKind.NamespaceKeyword:
case ts.SyntaxKind.ModuleKeyword:
case ts.SyntaxKind.TypeKeyword:
case ts.SyntaxKind.FunctionKeyword:
// Replace the stuff we possibly deleted above
let replacedModifiers: string = '';
if (entity.shouldInlineExport) {
replacedModifiers = 'export ' + replacedModifiers;
}
if (previousSpan && previousSpan.kind === ts.SyntaxKind.SyntaxList) {
// If there is a previous span of type SyntaxList, then apply it before any other modifiers
// (e.g. "abstract") that appear there.
previousSpan.modification.prefix = replacedModifiers + previousSpan.modification.prefix;
} else {
// Otherwise just stick it in front of this span
span.modification.prefix = replacedModifiers + span.modification.prefix;
}
break;
case ts.SyntaxKind.SyntaxList:
if (span.parent) {
if (AstDeclaration.isSupportedSyntaxKind(span.parent.kind)) {
// If the immediate parent is an API declaration, and the immediate children are API declarations,
// then sort the children alphabetically
sortChildren = true;
} else if (span.parent.kind === ts.SyntaxKind.ModuleBlock) {
// Namespaces are special because their chain goes ModuleDeclaration -> ModuleBlock -> SyntaxList
sortChildren = true;
}
}
break;
case ts.SyntaxKind.VariableDeclaration:
if (!span.parent) {
// The VariableDeclaration node is part of a VariableDeclarationList, however
// the Entry.followedSymbol points to the VariableDeclaration part because
// multiple definitions might share the same VariableDeclarationList.
//
// Since we are emitting a separate declaration for each one, we need to look upwards
// in the ts.Node tree and write a copy of the enclosing VariableDeclarationList
// content (e.g. "var" from "var x=1, y=2").
const list: ts.VariableDeclarationList | undefined = TypeScriptHelpers.matchAncestor(span.node, [
ts.SyntaxKind.VariableDeclarationList,
ts.SyntaxKind.VariableDeclaration
]);
if (!list) {
// This should not happen unless the compiler API changes somehow
throw new InternalError('Unsupported variable declaration');
}
const listPrefix: string = list
.getSourceFile()
.text.substring(list.getStart(), list.declarations[0].getStart());
span.modification.prefix = listPrefix + span.modification.prefix;
span.modification.suffix = ';';
if (entity.shouldInlineExport) {
span.modification.prefix = 'export ' + span.modification.prefix;
}
}
break;
case ts.SyntaxKind.Identifier:
const referencedEntity: CollectorEntity | undefined = collector.tryGetEntityForNode(
span.node as ts.Identifier
);
if (referencedEntity) {
if (!referencedEntity.nameForEmit) {
// This should never happen
throw new InternalError('referencedEntry.nameForEmit is undefined');
}
span.modification.prefix = referencedEntity.nameForEmit;
// For debugging:
// span.modification.prefix += '/*R=FIX*/';
} else {
// For debugging:
// span.modification.prefix += '/*R=KEEP*/';
}
break;
case ts.SyntaxKind.TypeLiteral:
insideTypeLiteral = true;
break;
case ts.SyntaxKind.ImportType:
DtsEmitHelpers.modifyImportTypeSpan(
collector,
span,
astDeclaration,
(childSpan, childAstDeclaration) => {
ApiReportGenerator._modifySpan(
collector,
childSpan,
entity,
childAstDeclaration,
insideTypeLiteral
);
}
);
break;
}
if (recurseChildren) {
for (const child of span.children) {
let childAstDeclaration: AstDeclaration = astDeclaration;
if (AstDeclaration.isSupportedSyntaxKind(child.kind)) {
childAstDeclaration = collector.astSymbolTable.getChildAstDeclarationByNode(
child.node,
astDeclaration
);
if (sortChildren) {
span.modification.sortChildren = true;
child.modification.sortKey = Collector.getSortKeyIgnoringUnderscore(
childAstDeclaration.astSymbol.localName
);
}
if (!insideTypeLiteral) {
const messagesToReport: ExtractorMessage[] =
collector.messageRouter.fetchAssociatedMessagesForReviewFile(childAstDeclaration);
const aedocSynopsis: string = ApiReportGenerator._getAedocSynopsis(
collector,
childAstDeclaration,
messagesToReport
);
child.modification.prefix = aedocSynopsis + child.modification.prefix;
}
}
ApiReportGenerator._modifySpan(collector, child, entity, childAstDeclaration, insideTypeLiteral);
}
}
}