A parser for the TypeScript doc comment syntax
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Utilities for rendering DocNode trees back to TSDoc markup or extracting plain text content from parsed comments.
Renders a DocNode tree back to TSDoc markup, preserving the original comment structure and formatting.
/**
* Renders a DocNode tree back to TSDoc markup
*/
class TSDocEmitter {
constructor();
/** Render any DocNode back to TSDoc format */
renderDocNode(docNode: DocNode): string;
/** Render a complete DocComment back to TSDoc format */
renderDocComment(docComment: DocComment): string;
}Usage Examples:
import { TSDocParser, TSDocEmitter } from "@microsoft/tsdoc";
const parser = new TSDocParser();
const emitter = new TSDocEmitter();
// Parse a comment
const context = parser.parseString(`
/**
* Calculates the area.
* @param width - The width value
* @param height - The height value
* @returns The calculated area
* @beta
* @example
* \`\`\`typescript
* const area = calculateArea(10, 20);
* console.log(area); // 200
* \`\`\`
*/
`);
// Render back to TSDoc
const renderedComment = emitter.renderDocComment(context.docComment);
console.log(renderedComment);
// Render individual sections
const remarksSection = context.docComment.remarksBlock;
if (remarksSection) {
const renderedRemarks = emitter.renderDocNode(remarksSection);
console.log("Remarks:", renderedRemarks);
}
// Render parameter blocks
context.docComment.parameterBlocks.forEach(paramBlock => {
const renderedParam = emitter.renderDocNode(paramBlock);
console.log("Parameter:", renderedParam);
});Extracts plain text from a DocNode tree, removing all TSDoc markup and formatting.
/**
* Extracts plain text from a DocNode tree, removing markup
*/
class PlainTextEmitter {
constructor();
/** Extract plain text from any DocNode */
renderDocNode(docNode: DocNode): string;
/** Extract plain text from a complete DocComment */
renderDocComment(docComment: DocComment): string;
}Usage Examples:
import { TSDocParser, PlainTextEmitter } from "@microsoft/tsdoc";
const parser = new TSDocParser();
const plainTextEmitter = new PlainTextEmitter();
// Parse a complex comment with markup
const context = parser.parseString(`
/**
* This method {@link MyClass.helper | helps} with calculations.
*
* It supports **bold** and \`inline code\` formatting.
*
* @param value - The input {@link InputType | value}
* @returns Processed result
*
* @example
* Here's how to use it:
* \`\`\`typescript
* const result = process(42);
* \`\`\`
*/
`);
// Extract plain text (removes all markup)
const plainText = plainTextEmitter.renderDocComment(context.docComment);
console.log("Plain text:", plainText);
// Output: "This method helps with calculations. It supports bold and inline code formatting."
// Extract plain text from specific sections
const summaryText = plainTextEmitter.renderDocNode(context.docComment.summarySection);
console.log("Summary:", summaryText);
// Extract parameter descriptions
context.docComment.parameterBlocks.forEach(paramBlock => {
const paramText = plainTextEmitter.renderDocNode(paramBlock.content);
console.log(`${paramBlock.parameterName}: ${paramText}`);
});Utility class for efficiently building strings with append operations. Used internally by emitters but also available for custom rendering logic.
/**
* Utility for efficiently building strings
*/
class StringBuilder implements IStringBuilder {
constructor();
/** Append text to the string being built */
append(text: string): void;
/** Get the final built string */
toString(): string;
}
/**
* Interface for string building operations
*/
interface IStringBuilder {
/** Append text to the string being built */
append(text: string): void;
/** Get the final built string */
toString(): string;
}Usage Examples:
import { StringBuilder } from "@microsoft/tsdoc";
// Basic string building
const builder = new StringBuilder();
builder.append("Hello");
builder.append(" ");
builder.append("world");
console.log(builder.toString()); // "Hello world"
// Building formatted output
function formatParameterList(paramBlocks: ReadonlyArray<DocParamBlock>): string {
const builder = new StringBuilder();
builder.append("Parameters:\n");
paramBlocks.forEach((param, index) => {
builder.append(` ${index + 1}. ${param.parameterName}\n`);
});
return builder.toString();
}
// Custom rendering with StringBuilder
function renderCustomSummary(docComment: DocComment): string {
const builder = new StringBuilder();
const plainEmitter = new PlainTextEmitter();
// Extract summary text
const summaryText = plainEmitter.renderDocNode(docComment.summarySection);
builder.append("Summary: ");
builder.append(summaryText);
// Add parameter count
if (docComment.parameterBlocks.length > 0) {
builder.append(`\nParameters: ${docComment.parameterBlocks.length}`);
}
// Add modifiers
const modifiers = docComment.modifierTagSet;
if (modifiers.isBeta) {
builder.append("\n[BETA API]");
}
if (modifiers.isDeprecated) {
builder.append("\n[DEPRECATED]");
}
return builder.toString();
}Create custom rendering by combining emitters with AST traversal:
import {
TSDocParser,
TSDocEmitter,
PlainTextEmitter,
DocNode,
DocCodeSpan,
DocFencedCode,
DocLinkTag
} from "@microsoft/tsdoc";
class CustomMarkdownEmitter {
private tsdocEmitter = new TSDocEmitter();
private plainEmitter = new PlainTextEmitter();
renderAsMarkdown(docComment: DocComment): string {
const builder = new StringBuilder();
// Render summary as markdown
const summaryText = this.plainEmitter.renderDocNode(docComment.summarySection);
builder.append(`## Summary\n\n${summaryText}\n\n`);
// Render parameters
if (docComment.parameterBlocks.length > 0) {
builder.append("## Parameters\n\n");
docComment.parameterBlocks.forEach(param => {
const description = this.plainEmitter.renderDocNode(param.content);
builder.append(`- **${param.parameterName}**: ${description}\n`);
});
builder.append("\n");
}
// Render examples
if (docComment.exampleBlocks.length > 0) {
builder.append("## Examples\n\n");
docComment.exampleBlocks.forEach((example, index) => {
const exampleText = this.extractCodeBlocks(example.content);
builder.append(`### Example ${index + 1}\n\n${exampleText}\n\n`);
});
}
return builder.toString();
}
private extractCodeBlocks(section: DocSection): string {
// Custom logic to find and format code blocks
const builder = new StringBuilder();
const findCodeNodes = (node: DocNode): void => {
if (node instanceof DocFencedCode) {
builder.append(`\`\`\`${node.language}\n${node.code}\n\`\`\`\n`);
} else if (node instanceof DocCodeSpan) {
builder.append(`\`${node.code}\`\n`);
}
for (const child of node.getChildNodes()) {
if (child) {
findCodeNodes(child);
}
}
};
findCodeNodes(section);
return builder.toString();
}
}
// Usage
const parser = new TSDocParser();
const customEmitter = new CustomMarkdownEmitter();
const context = parser.parseString("/** ... */");
const markdown = customEmitter.renderAsMarkdown(context.docComment);Extract specific types of content from comments:
class ContentExtractor {
private plainEmitter = new PlainTextEmitter();
/** Extract all code examples from a comment */
extractCodeExamples(docComment: DocComment): string[] {
const examples: string[] = [];
const extractFromNode = (node: DocNode): void => {
if (node instanceof DocFencedCode) {
examples.push(node.code);
}
for (const child of node.getChildNodes()) {
if (child) {
extractFromNode(child);
}
}
};
// Extract from example blocks
docComment.exampleBlocks.forEach(block => {
extractFromNode(block.content);
});
return examples;
}
/** Extract all @see references */
extractSeeReferences(docComment: DocComment): string[] {
return docComment.seeBlocks.map(block =>
this.plainEmitter.renderDocNode(block.content).trim()
);
}
/** Extract parameter information */
extractParameterInfo(docComment: DocComment): Array<{name: string, description: string}> {
return docComment.parameterBlocks.map(param => ({
name: param.parameterName,
description: this.plainEmitter.renderDocNode(param.content).trim()
}));
}
/** Get API stability information */
getStabilityInfo(docComment: DocComment): {
stability: 'stable' | 'beta' | 'alpha' | 'experimental' | 'deprecated';
isInternal: boolean;
} {
const modifiers = docComment.modifierTagSet;
let stability: 'stable' | 'beta' | 'alpha' | 'experimental' | 'deprecated' = 'stable';
if (modifiers.isAlpha) stability = 'alpha';
else if (modifiers.isBeta) stability = 'beta';
else if (modifiers.isExperimental) stability = 'experimental';
else if (docComment.deprecatedBlock) stability = 'deprecated';
return {
stability,
isInternal: modifiers.isInternal
};
}
}