@microsoft/api-documenter
Version:
Read JSON files from api-extractor, generate documentation pages
999 lines (998 loc) • 50.5 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MarkdownDocumenter = void 0;
const path = __importStar(require("path"));
const node_core_library_1 = require("@rushstack/node-core-library");
const tsdoc_1 = require("@microsoft/tsdoc");
const api_extractor_model_1 = require("@microsoft/api-extractor-model");
const CustomDocNodeKind_1 = require("../nodes/CustomDocNodeKind");
const DocHeading_1 = require("../nodes/DocHeading");
const DocTable_1 = require("../nodes/DocTable");
const DocEmphasisSpan_1 = require("../nodes/DocEmphasisSpan");
const DocTableRow_1 = require("../nodes/DocTableRow");
const DocTableCell_1 = require("../nodes/DocTableCell");
const DocNoteBox_1 = require("../nodes/DocNoteBox");
const Utilities_1 = require("../utils/Utilities");
const CustomMarkdownEmitter_1 = require("../markdown/CustomMarkdownEmitter");
const PluginLoader_1 = require("../plugin/PluginLoader");
const MarkdownDocumenterFeature_1 = require("../plugin/MarkdownDocumenterFeature");
const MarkdownDocumenterAccessor_1 = require("../plugin/MarkdownDocumenterAccessor");
/**
* Renders API documentation in the Markdown file format.
* For more info: https://en.wikipedia.org/wiki/Markdown
*/
class MarkdownDocumenter {
constructor(options) {
this._apiModel = options.apiModel;
this._documenterConfig = options.documenterConfig;
this._outputFolder = options.outputFolder;
this._tsdocConfiguration = CustomDocNodeKind_1.CustomDocNodes.configuration;
this._markdownEmitter = new CustomMarkdownEmitter_1.CustomMarkdownEmitter(this._apiModel);
this._pluginLoader = new PluginLoader_1.PluginLoader();
}
generateFiles() {
if (this._documenterConfig) {
this._pluginLoader.load(this._documenterConfig, () => {
return new MarkdownDocumenterFeature_1.MarkdownDocumenterFeatureContext({
apiModel: this._apiModel,
outputFolder: this._outputFolder,
documenter: new MarkdownDocumenterAccessor_1.MarkdownDocumenterAccessor({
getLinkForApiItem: (apiItem) => {
return this._getLinkFilenameForApiItem(apiItem);
}
})
});
});
}
console.log();
this._deleteOldOutputFiles();
this._writeApiItemPage(this._apiModel);
if (this._pluginLoader.markdownDocumenterFeature) {
this._pluginLoader.markdownDocumenterFeature.onFinished({});
}
}
_writeApiItemPage(apiItem) {
const configuration = this._tsdocConfiguration;
const output = new tsdoc_1.DocSection({ configuration });
this._writeBreadcrumb(output, apiItem);
const scopedName = apiItem.getScopedNameWithinPackage();
switch (apiItem.kind) {
case api_extractor_model_1.ApiItemKind.Class:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} class` }));
break;
case api_extractor_model_1.ApiItemKind.Enum:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} enum` }));
break;
case api_extractor_model_1.ApiItemKind.Interface:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} interface` }));
break;
case api_extractor_model_1.ApiItemKind.Constructor:
case api_extractor_model_1.ApiItemKind.ConstructSignature:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: scopedName }));
break;
case api_extractor_model_1.ApiItemKind.Method:
case api_extractor_model_1.ApiItemKind.MethodSignature:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} method` }));
break;
case api_extractor_model_1.ApiItemKind.Function:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} function` }));
break;
case api_extractor_model_1.ApiItemKind.Model:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `API Reference` }));
break;
case api_extractor_model_1.ApiItemKind.Namespace:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} namespace` }));
break;
case api_extractor_model_1.ApiItemKind.Package:
console.log(`Writing ${apiItem.displayName} package`);
const unscopedPackageName = node_core_library_1.PackageName.getUnscopedName(apiItem.displayName);
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${unscopedPackageName} package` }));
break;
case api_extractor_model_1.ApiItemKind.Property:
case api_extractor_model_1.ApiItemKind.PropertySignature:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} property` }));
break;
case api_extractor_model_1.ApiItemKind.TypeAlias:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} type` }));
break;
case api_extractor_model_1.ApiItemKind.Variable:
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: `${scopedName} variable` }));
break;
default:
throw new Error('Unsupported API item kind: ' + apiItem.kind);
}
if (api_extractor_model_1.ApiReleaseTagMixin.isBaseClassOf(apiItem)) {
if (apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Alpha) {
this._writeAlphaWarning(output);
}
else if (apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Beta) {
this._writeBetaWarning(output);
}
}
const decoratorBlocks = [];
if (apiItem instanceof api_extractor_model_1.ApiDocumentedItem) {
const tsdocComment = apiItem.tsdocComment;
if (tsdocComment) {
decoratorBlocks.push(...tsdocComment.customBlocks.filter((block) => block.blockTag.tagNameWithUpperCase === tsdoc_1.StandardTags.decorator.tagNameWithUpperCase));
if (tsdocComment.deprecatedBlock) {
output.appendNode(new DocNoteBox_1.DocNoteBox({ configuration }, [
new tsdoc_1.DocParagraph({ configuration }, [
new tsdoc_1.DocPlainText({
configuration,
text: 'Warning: This API is now obsolete. '
})
]),
...tsdocComment.deprecatedBlock.content.nodes
]));
}
this._appendSection(output, tsdocComment.summarySection);
}
}
if (apiItem instanceof api_extractor_model_1.ApiDeclaredItem) {
if (apiItem.excerpt.text.length > 0) {
output.appendNode(new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'Signature:' })
])
]));
output.appendNode(new tsdoc_1.DocFencedCode({
configuration,
code: apiItem.getExcerptWithModifiers(),
language: 'typescript'
}));
}
this._writeHeritageTypes(output, apiItem);
}
if (decoratorBlocks.length > 0) {
output.appendNode(new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'Decorators:' })
])
]));
for (const decoratorBlock of decoratorBlocks) {
output.appendNodes(decoratorBlock.content.nodes);
}
}
let appendRemarks = true;
switch (apiItem.kind) {
case api_extractor_model_1.ApiItemKind.Class:
case api_extractor_model_1.ApiItemKind.Interface:
case api_extractor_model_1.ApiItemKind.Namespace:
case api_extractor_model_1.ApiItemKind.Package:
this._writeRemarksSection(output, apiItem);
appendRemarks = false;
break;
}
switch (apiItem.kind) {
case api_extractor_model_1.ApiItemKind.Class:
this._writeClassTables(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Enum:
this._writeEnumTables(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Interface:
this._writeInterfaceTables(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Constructor:
case api_extractor_model_1.ApiItemKind.ConstructSignature:
case api_extractor_model_1.ApiItemKind.Method:
case api_extractor_model_1.ApiItemKind.MethodSignature:
case api_extractor_model_1.ApiItemKind.Function:
this._writeParameterTables(output, apiItem);
this._writeThrowsSection(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Namespace:
this._writePackageOrNamespaceTables(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Model:
this._writeModelTable(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Package:
this._writePackageOrNamespaceTables(output, apiItem);
break;
case api_extractor_model_1.ApiItemKind.Property:
case api_extractor_model_1.ApiItemKind.PropertySignature:
break;
case api_extractor_model_1.ApiItemKind.TypeAlias:
break;
case api_extractor_model_1.ApiItemKind.Variable:
break;
default:
throw new Error('Unsupported API item kind: ' + apiItem.kind);
}
if (appendRemarks) {
this._writeRemarksSection(output, apiItem);
}
const filename = path.join(this._outputFolder, this._getFilenameForApiItem(apiItem));
const stringBuilder = new tsdoc_1.StringBuilder();
stringBuilder.append('<!-- Do not edit this file. It is automatically generated by API Documenter. -->\n\n');
this._markdownEmitter.emit(stringBuilder, output, {
contextApiItem: apiItem,
onGetFilenameForApiItem: (apiItemForFilename) => {
return this._getLinkFilenameForApiItem(apiItemForFilename);
}
});
let pageContent = stringBuilder.toString();
if (this._pluginLoader.markdownDocumenterFeature) {
// Allow the plugin to customize the pageContent
const eventArgs = {
apiItem: apiItem,
outputFilename: filename,
pageContent: pageContent
};
this._pluginLoader.markdownDocumenterFeature.onBeforeWritePage(eventArgs);
pageContent = eventArgs.pageContent;
}
node_core_library_1.FileSystem.writeFile(filename, pageContent, {
convertLineEndings: this._documenterConfig ? this._documenterConfig.newlineKind : node_core_library_1.NewlineKind.CrLf
});
}
_writeHeritageTypes(output, apiItem) {
const configuration = this._tsdocConfiguration;
if (apiItem instanceof api_extractor_model_1.ApiClass) {
if (apiItem.extendsType) {
const extendsParagraph = new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'Extends: ' })
])
]);
this._appendExcerptWithHyperlinks(extendsParagraph, apiItem.extendsType.excerpt);
output.appendNode(extendsParagraph);
}
if (apiItem.implementsTypes.length > 0) {
const implementsParagraph = new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'Implements: ' })
])
]);
let needsComma = false;
for (const implementsType of apiItem.implementsTypes) {
if (needsComma) {
implementsParagraph.appendNode(new tsdoc_1.DocPlainText({ configuration, text: ', ' }));
}
this._appendExcerptWithHyperlinks(implementsParagraph, implementsType.excerpt);
needsComma = true;
}
output.appendNode(implementsParagraph);
}
}
if (apiItem instanceof api_extractor_model_1.ApiInterface) {
if (apiItem.extendsTypes.length > 0) {
const extendsParagraph = new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'Extends: ' })
])
]);
let needsComma = false;
for (const extendsType of apiItem.extendsTypes) {
if (needsComma) {
extendsParagraph.appendNode(new tsdoc_1.DocPlainText({ configuration, text: ', ' }));
}
this._appendExcerptWithHyperlinks(extendsParagraph, extendsType.excerpt);
needsComma = true;
}
output.appendNode(extendsParagraph);
}
}
if (apiItem instanceof api_extractor_model_1.ApiTypeAlias) {
const refs = apiItem.excerptTokens.filter((token) => token.kind === api_extractor_model_1.ExcerptTokenKind.Reference &&
token.canonicalReference &&
this._apiModel.resolveDeclarationReference(token.canonicalReference, undefined).resolvedApiItem);
if (refs.length > 0) {
const referencesParagraph = new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'References: ' })
])
]);
let needsComma = false;
const visited = new Set();
for (const ref of refs) {
if (visited.has(ref.text)) {
continue;
}
visited.add(ref.text);
if (needsComma) {
referencesParagraph.appendNode(new tsdoc_1.DocPlainText({ configuration, text: ', ' }));
}
this._appendExcerptTokenWithHyperlinks(referencesParagraph, ref);
needsComma = true;
}
output.appendNode(referencesParagraph);
}
}
}
_writeRemarksSection(output, apiItem) {
const configuration = this._tsdocConfiguration;
if (apiItem instanceof api_extractor_model_1.ApiDocumentedItem) {
const tsdocComment = apiItem.tsdocComment;
if (tsdocComment) {
// Write the @remarks block
if (tsdocComment.remarksBlock) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Remarks' }));
this._appendSection(output, tsdocComment.remarksBlock.content);
}
// Write the @example blocks
const exampleBlocks = tsdocComment.customBlocks.filter((x) => x.blockTag.tagNameWithUpperCase === tsdoc_1.StandardTags.example.tagNameWithUpperCase);
let exampleNumber = 1;
for (const exampleBlock of exampleBlocks) {
const heading = exampleBlocks.length > 1 ? `Example ${exampleNumber}` : 'Example';
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: heading }));
this._appendSection(output, exampleBlock.content);
++exampleNumber;
}
}
}
}
_writeThrowsSection(output, apiItem) {
const configuration = this._tsdocConfiguration;
if (apiItem instanceof api_extractor_model_1.ApiDocumentedItem) {
const tsdocComment = apiItem.tsdocComment;
if (tsdocComment) {
// Write the @throws blocks
const throwsBlocks = tsdocComment.customBlocks.filter((x) => x.blockTag.tagNameWithUpperCase === tsdoc_1.StandardTags.throws.tagNameWithUpperCase);
if (throwsBlocks.length > 0) {
const heading = 'Exceptions';
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: heading }));
for (const throwsBlock of throwsBlocks) {
this._appendSection(output, throwsBlock.content);
}
}
}
}
}
/**
* GENERATE PAGE: MODEL
*/
_writeModelTable(output, apiModel) {
const configuration = this._tsdocConfiguration;
const packagesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Package', 'Description']
});
for (const apiMember of apiModel.members) {
const row = new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createDescriptionCell(apiMember)
]);
switch (apiMember.kind) {
case api_extractor_model_1.ApiItemKind.Package:
packagesTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
}
}
if (packagesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Packages' }));
output.appendNode(packagesTable);
}
}
/**
* GENERATE PAGE: PACKAGE or NAMESPACE
*/
_writePackageOrNamespaceTables(output, apiContainer) {
const configuration = this._tsdocConfiguration;
const abstractClassesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Abstract Class', 'Description']
});
const classesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Class', 'Description']
});
const enumerationsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Enumeration', 'Description']
});
const functionsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Function', 'Description']
});
const interfacesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Interface', 'Description']
});
const namespacesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Namespace', 'Description']
});
const variablesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Variable', 'Description']
});
const typeAliasesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Type Alias', 'Description']
});
const apiMembers = apiContainer.kind === api_extractor_model_1.ApiItemKind.Package
? apiContainer.entryPoints[0].members
: apiContainer.members;
for (const apiMember of apiMembers) {
const row = new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createDescriptionCell(apiMember)
]);
switch (apiMember.kind) {
case api_extractor_model_1.ApiItemKind.Class:
if (api_extractor_model_1.ApiAbstractMixin.isBaseClassOf(apiMember) && apiMember.isAbstract) {
abstractClassesTable.addRow(row);
}
else {
classesTable.addRow(row);
}
this._writeApiItemPage(apiMember);
break;
case api_extractor_model_1.ApiItemKind.Enum:
enumerationsTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
case api_extractor_model_1.ApiItemKind.Interface:
interfacesTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
case api_extractor_model_1.ApiItemKind.Namespace:
namespacesTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
case api_extractor_model_1.ApiItemKind.Function:
functionsTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
case api_extractor_model_1.ApiItemKind.TypeAlias:
typeAliasesTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
case api_extractor_model_1.ApiItemKind.Variable:
variablesTable.addRow(row);
this._writeApiItemPage(apiMember);
break;
}
}
if (classesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Classes' }));
output.appendNode(classesTable);
}
if (abstractClassesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Abstract Classes' }));
output.appendNode(abstractClassesTable);
}
if (enumerationsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Enumerations' }));
output.appendNode(enumerationsTable);
}
if (functionsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Functions' }));
output.appendNode(functionsTable);
}
if (interfacesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Interfaces' }));
output.appendNode(interfacesTable);
}
if (namespacesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Namespaces' }));
output.appendNode(namespacesTable);
}
if (variablesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Variables' }));
output.appendNode(variablesTable);
}
if (typeAliasesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Type Aliases' }));
output.appendNode(typeAliasesTable);
}
}
/**
* GENERATE PAGE: CLASS
*/
_writeClassTables(output, apiClass) {
const configuration = this._tsdocConfiguration;
const eventsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Property', 'Modifiers', 'Type', 'Description']
});
const constructorsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Constructor', 'Modifiers', 'Description']
});
const propertiesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Property', 'Modifiers', 'Type', 'Description']
});
const methodsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Method', 'Modifiers', 'Description']
});
const apiMembers = this._getMembersAndWriteIncompleteWarning(apiClass, output);
for (const apiMember of apiMembers) {
const isInherited = apiMember.parent !== apiClass;
switch (apiMember.kind) {
case api_extractor_model_1.ApiItemKind.Constructor: {
constructorsTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createModifiersCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
this._writeApiItemPage(apiMember);
break;
}
case api_extractor_model_1.ApiItemKind.Method: {
methodsTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createModifiersCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
this._writeApiItemPage(apiMember);
break;
}
case api_extractor_model_1.ApiItemKind.Property: {
if (apiMember.isEventProperty) {
eventsTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createModifiersCell(apiMember),
this._createPropertyTypeCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
}
else {
propertiesTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createModifiersCell(apiMember),
this._createPropertyTypeCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
}
this._writeApiItemPage(apiMember);
break;
}
}
}
if (eventsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Events' }));
output.appendNode(eventsTable);
}
if (constructorsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Constructors' }));
output.appendNode(constructorsTable);
}
if (propertiesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Properties' }));
output.appendNode(propertiesTable);
}
if (methodsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Methods' }));
output.appendNode(methodsTable);
}
}
/**
* GENERATE PAGE: ENUM
*/
_writeEnumTables(output, apiEnum) {
const configuration = this._tsdocConfiguration;
const enumMembersTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Member', 'Value', 'Description']
});
for (const apiEnumMember of apiEnum.members) {
enumMembersTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
new DocTableCell_1.DocTableCell({ configuration }, [
new tsdoc_1.DocParagraph({ configuration }, [
new tsdoc_1.DocPlainText({ configuration, text: Utilities_1.Utilities.getConciseSignature(apiEnumMember) })
])
]),
this._createInitializerCell(apiEnumMember),
this._createDescriptionCell(apiEnumMember)
]));
}
if (enumMembersTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Enumeration Members' }));
output.appendNode(enumMembersTable);
}
}
/**
* GENERATE PAGE: INTERFACE
*/
_writeInterfaceTables(output, apiInterface) {
const configuration = this._tsdocConfiguration;
const eventsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Property', 'Modifiers', 'Type', 'Description']
});
const propertiesTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Property', 'Modifiers', 'Type', 'Description']
});
const methodsTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Method', 'Description']
});
const apiMembers = this._getMembersAndWriteIncompleteWarning(apiInterface, output);
for (const apiMember of apiMembers) {
const isInherited = apiMember.parent !== apiInterface;
switch (apiMember.kind) {
case api_extractor_model_1.ApiItemKind.ConstructSignature:
case api_extractor_model_1.ApiItemKind.MethodSignature: {
methodsTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
this._writeApiItemPage(apiMember);
break;
}
case api_extractor_model_1.ApiItemKind.PropertySignature: {
if (apiMember.isEventProperty) {
eventsTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createModifiersCell(apiMember),
this._createPropertyTypeCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
}
else {
propertiesTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
this._createTitleCell(apiMember),
this._createModifiersCell(apiMember),
this._createPropertyTypeCell(apiMember),
this._createDescriptionCell(apiMember, isInherited)
]));
}
this._writeApiItemPage(apiMember);
break;
}
}
}
if (eventsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Events' }));
output.appendNode(eventsTable);
}
if (propertiesTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Properties' }));
output.appendNode(propertiesTable);
}
if (methodsTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Methods' }));
output.appendNode(methodsTable);
}
}
/**
* GENERATE PAGE: FUNCTION-LIKE
*/
_writeParameterTables(output, apiParameterListMixin) {
const configuration = this._tsdocConfiguration;
const parametersTable = new DocTable_1.DocTable({
configuration,
headerTitles: ['Parameter', 'Type', 'Description']
});
for (const apiParameter of apiParameterListMixin.parameters) {
const parameterDescription = new tsdoc_1.DocSection({ configuration });
if (apiParameter.isOptional) {
parameterDescription.appendNodesInParagraph([
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, italic: true }, [
new tsdoc_1.DocPlainText({ configuration, text: '(Optional)' })
]),
new tsdoc_1.DocPlainText({ configuration, text: ' ' })
]);
}
if (apiParameter.tsdocParamBlock) {
this._appendAndMergeSection(parameterDescription, apiParameter.tsdocParamBlock.content);
}
parametersTable.addRow(new DocTableRow_1.DocTableRow({ configuration }, [
new DocTableCell_1.DocTableCell({ configuration }, [
new tsdoc_1.DocParagraph({ configuration }, [
new tsdoc_1.DocPlainText({ configuration, text: apiParameter.name })
])
]),
new DocTableCell_1.DocTableCell({ configuration }, [
this._createParagraphForTypeExcerpt(apiParameter.parameterTypeExcerpt)
]),
new DocTableCell_1.DocTableCell({ configuration }, parameterDescription.nodes)
]));
}
if (parametersTable.rows.length > 0) {
output.appendNode(new DocHeading_1.DocHeading({ configuration, title: 'Parameters' }));
output.appendNode(parametersTable);
}
if (api_extractor_model_1.ApiReturnTypeMixin.isBaseClassOf(apiParameterListMixin)) {
const returnTypeExcerpt = apiParameterListMixin.returnTypeExcerpt;
output.appendNode(new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true }, [
new tsdoc_1.DocPlainText({ configuration, text: 'Returns:' })
])
]));
output.appendNode(this._createParagraphForTypeExcerpt(returnTypeExcerpt));
if (apiParameterListMixin instanceof api_extractor_model_1.ApiDocumentedItem) {
if (apiParameterListMixin.tsdocComment && apiParameterListMixin.tsdocComment.returnsBlock) {
this._appendSection(output, apiParameterListMixin.tsdocComment.returnsBlock.content);
}
}
}
}
_createParagraphForTypeExcerpt(excerpt) {
const configuration = this._tsdocConfiguration;
const paragraph = new tsdoc_1.DocParagraph({ configuration });
if (!excerpt.text.trim()) {
paragraph.appendNode(new tsdoc_1.DocPlainText({ configuration, text: '(not declared)' }));
}
else {
this._appendExcerptWithHyperlinks(paragraph, excerpt);
}
return paragraph;
}
_appendExcerptWithHyperlinks(docNodeContainer, excerpt) {
for (const token of excerpt.spannedTokens) {
this._appendExcerptTokenWithHyperlinks(docNodeContainer, token);
}
}
_appendExcerptTokenWithHyperlinks(docNodeContainer, token) {
const configuration = this._tsdocConfiguration;
// Markdown doesn't provide a standardized syntax for hyperlinks inside code spans, so we will render
// the type expression as DocPlainText. Instead of creating multiple DocParagraphs, we can simply
// discard any newlines and let the renderer do normal word-wrapping.
const unwrappedTokenText = token.text.replace(/[\r\n]+/g, ' ');
// If it's hyperlinkable, then append a DocLinkTag
if (token.kind === api_extractor_model_1.ExcerptTokenKind.Reference && token.canonicalReference) {
const apiItemResult = this._apiModel.resolveDeclarationReference(token.canonicalReference, undefined);
if (apiItemResult.resolvedApiItem) {
docNodeContainer.appendNode(new tsdoc_1.DocLinkTag({
configuration,
tagName: '@link',
linkText: unwrappedTokenText,
urlDestination: this._getLinkFilenameForApiItem(apiItemResult.resolvedApiItem)
}));
return;
}
}
// Otherwise append non-hyperlinked text
docNodeContainer.appendNode(new tsdoc_1.DocPlainText({ configuration, text: unwrappedTokenText }));
}
_createTitleCell(apiItem) {
const configuration = this._tsdocConfiguration;
let linkText = Utilities_1.Utilities.getConciseSignature(apiItem);
if (api_extractor_model_1.ApiOptionalMixin.isBaseClassOf(apiItem) && apiItem.isOptional) {
linkText += '?';
}
return new DocTableCell_1.DocTableCell({ configuration }, [
new tsdoc_1.DocParagraph({ configuration }, [
new tsdoc_1.DocLinkTag({
configuration,
tagName: '@link',
linkText: linkText,
urlDestination: this._getLinkFilenameForApiItem(apiItem)
})
])
]);
}
/**
* This generates a DocTableCell for an ApiItem including the summary section and "(BETA)" annotation.
*
* @remarks
* We mostly assume that the input is an ApiDocumentedItem, but it's easier to perform this as a runtime
* check than to have each caller perform a type cast.
*/
_createDescriptionCell(apiItem, isInherited = false) {
const configuration = this._tsdocConfiguration;
const section = new tsdoc_1.DocSection({ configuration });
if (api_extractor_model_1.ApiReleaseTagMixin.isBaseClassOf(apiItem)) {
if (apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Alpha || apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Beta) {
section.appendNodesInParagraph([
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, bold: true, italic: true }, [
new tsdoc_1.DocPlainText({
configuration,
text: `(${apiItem.releaseTag === api_extractor_model_1.ReleaseTag.Alpha ? 'ALPHA' : 'BETA'})`
})
]),
new tsdoc_1.DocPlainText({ configuration, text: ' ' })
]);
}
}
if (api_extractor_model_1.ApiOptionalMixin.isBaseClassOf(apiItem) && apiItem.isOptional) {
section.appendNodesInParagraph([
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, italic: true }, [
new tsdoc_1.DocPlainText({ configuration, text: '(Optional)' })
]),
new tsdoc_1.DocPlainText({ configuration, text: ' ' })
]);
}
if (apiItem instanceof api_extractor_model_1.ApiDocumentedItem) {
if (apiItem.tsdocComment !== undefined) {
this._appendAndMergeSection(section, apiItem.tsdocComment.summarySection);
}
}
if (isInherited && apiItem.parent) {
section.appendNode(new tsdoc_1.DocParagraph({ configuration }, [
new tsdoc_1.DocPlainText({ configuration, text: '(Inherited from ' }),
new tsdoc_1.DocLinkTag({
configuration,
tagName: '@link',
linkText: apiItem.parent.displayName,
urlDestination: this._getLinkFilenameForApiItem(apiItem.parent)
}),
new tsdoc_1.DocPlainText({ configuration, text: ')' })
]));
}
return new DocTableCell_1.DocTableCell({ configuration }, section.nodes);
}
_createModifiersCell(apiItem) {
const configuration = this._tsdocConfiguration;
const section = new tsdoc_1.DocSection({ configuration });
// Output modifiers in syntactically correct order: first access modifier (here: `protected`), then
// `static` or `abstract` (no member can be both, so the order between the two of them does not matter),
// last `readonly`. If `override` was supported, it would go directly before `readonly`.
if (api_extractor_model_1.ApiProtectedMixin.isBaseClassOf(apiItem)) {
if (apiItem.isProtected) {
section.appendNode(new tsdoc_1.DocParagraph({ configuration }, [new tsdoc_1.DocCodeSpan({ configuration, code: 'protected' })]));
}
}
if (api_extractor_model_1.ApiStaticMixin.isBaseClassOf(apiItem)) {
if (apiItem.isStatic) {
section.appendNode(new tsdoc_1.DocParagraph({ configuration }, [new tsdoc_1.DocCodeSpan({ configuration, code: 'static' })]));
}
}
if (api_extractor_model_1.ApiAbstractMixin.isBaseClassOf(apiItem)) {
if (apiItem.isAbstract) {
section.appendNode(new tsdoc_1.DocParagraph({ configuration }, [new tsdoc_1.DocCodeSpan({ configuration, code: 'abstract' })]));
}
}
if (api_extractor_model_1.ApiReadonlyMixin.isBaseClassOf(apiItem)) {
if (apiItem.isReadonly) {
section.appendNode(new tsdoc_1.DocParagraph({ configuration }, [new tsdoc_1.DocCodeSpan({ configuration, code: 'readonly' })]));
}
}
return new DocTableCell_1.DocTableCell({ configuration }, section.nodes);
}
_createPropertyTypeCell(apiItem) {
const configuration = this._tsdocConfiguration;
const section = new tsdoc_1.DocSection({ configuration });
if (apiItem instanceof api_extractor_model_1.ApiPropertyItem) {
section.appendNode(this._createParagraphForTypeExcerpt(apiItem.propertyTypeExcerpt));
}
return new DocTableCell_1.DocTableCell({ configuration }, section.nodes);
}
_createInitializerCell(apiItem) {
const configuration = this._tsdocConfiguration;
const section = new tsdoc_1.DocSection({ configuration });
if (api_extractor_model_1.ApiInitializerMixin.isBaseClassOf(apiItem)) {
if (apiItem.initializerExcerpt) {
section.appendNodeInParagraph(new tsdoc_1.DocCodeSpan({ configuration, code: apiItem.initializerExcerpt.text }));
}
}
return new DocTableCell_1.DocTableCell({ configuration }, section.nodes);
}
_writeBreadcrumb(output, apiItem) {
const configuration = this._tsdocConfiguration;
output.appendNodeInParagraph(new tsdoc_1.DocLinkTag({
configuration,
tagName: '@link',
linkText: 'Home',
urlDestination: this._getLinkFilenameForApiItem(this._apiModel)
}));
for (const hierarchyItem of apiItem.getHierarchy()) {
switch (hierarchyItem.kind) {
case api_extractor_model_1.ApiItemKind.Model:
case api_extractor_model_1.ApiItemKind.EntryPoint:
// We don't show the model as part of the breadcrumb because it is the root-level container.
// We don't show the entry point because today API Extractor doesn't support multiple entry points;
// this may change in the future.
break;
default:
output.appendNodesInParagraph([
new tsdoc_1.DocPlainText({
configuration,
text: ' > '
}),
new tsdoc_1.DocLinkTag({
configuration,
tagName: '@link',
linkText: hierarchyItem.displayName,
urlDestination: this._getLinkFilenameForApiItem(hierarchyItem)
})
]);
}
}
}
_writeAlphaWarning(output) {
const configuration = this._tsdocConfiguration;
const betaWarning = 'This API is provided as an alpha preview for developers and may change' +
' based on feedback that we receive. Do not use this API in a production environment.';
output.appendNode(new DocNoteBox_1.DocNoteBox({ configuration }, [
new tsdoc_1.DocParagraph({ configuration }, [new tsdoc_1.DocPlainText({ configuration, text: betaWarning })])
]));
}
_writeBetaWarning(output) {
const configuration = this._tsdocConfiguration;
const betaWarning = 'This API is provided as a beta preview for developers and may change' +
' based on feedback that we receive. Do not use this API in a production environment.';
output.appendNode(new DocNoteBox_1.DocNoteBox({ configuration }, [
new tsdoc_1.DocParagraph({ configuration }, [new tsdoc_1.DocPlainText({ configuration, text: betaWarning })])
]));
}
_appendSection(output, docSection) {
for (const node of docSection.nodes) {
output.appendNode(node);
}
}
_appendAndMergeSection(output, docSection) {
let firstNode = true;
for (const node of docSection.nodes) {
if (firstNode) {
if (node.kind === tsdoc_1.DocNodeKind.Paragraph) {
output.appendNodesInParagraph(node.getChildNodes());
firstNode = false;
continue;
}
}
firstNode = false;
output.appendNode(node);
}
}
_getMembersAndWriteIncompleteWarning(apiClassOrInterface, output) {
var _a;
const configuration = this._tsdocConfiguration;
const showInheritedMembers = !!((_a = this._documenterConfig) === null || _a === void 0 ? void 0 : _a.configFile.showInheritedMembers);
if (!showInheritedMembers) {
return apiClassOrInterface.members;
}
const result = apiClassOrInterface.findMembersWithInheritance();
// If the result is potentially incomplete, write a short warning communicating this.
if (result.maybeIncompleteResult) {
output.appendNode(new tsdoc_1.DocParagraph({ configuration }, [
new DocEmphasisSpan_1.DocEmphasisSpan({ configuration, italic: true }, [
new tsdoc_1.DocPlainText({
configuration,
text: '(Some inherited members may not be shown because they are not represented in the documentation.)'
})
])
]));
}
// Log the messages for diagnostic purposes.
for (const message of result.messages) {
console.log(`Diagnostic message for findMembersWithInheritance: ${message.text}`);
}
return result.items;
}
_getFilenameForApiItem(apiItem) {
if (apiItem.kind === api_extractor_model_1.ApiItemKind.Model) {
return 'index.md';
}
let baseName = '';
for (const hierarchyItem of apiItem.getHierarchy()) {
// For overloaded methods, add a suffix such as "MyClass.myMethod_2".
let qualifiedName = Utilities_1.Utilities.getSafeFilenameForName(hierarchyItem.displayName);
if (api_extractor_model_1.ApiParameterListMixin.isBaseClassOf(hierarchyItem)) {
if (hierarchyItem.overloadIndex > 1) {
// Subtract one for compatibility with earlier releases of API Documenter.
// (This will get revamped when we fix GitHub issue #1308)
qualifiedName += `_${hierarchyItem.overloadIndex - 1}`;
}
}
switch (hierarchyItem.kind) {
case api_extractor_model_1.ApiItemKind.Model:
case api_extractor_model_1.ApiItemKind.EntryPoint:
case api_extractor_model_1.ApiItemKind.EnumMember:
break;
case api_extractor_model_1.ApiItemKind.Package:
baseName = Utilities_1.Utilities.getSafeFilenameForName(node_core_library_1.PackageName.getUnscopedName(hierarchyItem.displayName));
break;