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
Comprehensive set of 45+ DocNode classes representing all parts of TSDoc comments including text, tags, code blocks, links, and HTML elements.
Abstract base class for all AST nodes in the TSDoc syntax tree.
/**
* Base class for all AST nodes in the TSDoc syntax tree
*/
abstract class DocNode {
/** The kind of DocNode (e.g., "DocComment", "DocParagraph") */
readonly kind: string;
/** Parent node in the AST, undefined for root nodes */
readonly parent: DocNode | undefined;
/** Get immediate child nodes, some positions may be undefined */
abstract getChildNodes(): ReadonlyArray<DocNode | undefined>;
}Root node representing an entire TSDoc comment with all its sections.
/**
* Root node representing an entire TSDoc comment
*/
class DocComment extends DocNode {
/** The summary section (first paragraph) */
readonly summarySection: DocSection;
/** The @remarks block, if present */
readonly remarksBlock: DocBlock | undefined;
/** All @param blocks */
readonly parameterBlocks: ReadonlyArray<DocParamBlock>;
/** All @typeParam blocks */
readonly typeParameterBlocks: ReadonlyArray<DocParamBlock>;
/** The @returns block, if present */
readonly returnsBlock: DocBlock | undefined;
/** The {@inheritDoc} tag, if present */
readonly inheritDocTag: DocInheritDocTag | undefined;
/** The @deprecated block, if present */
readonly deprecatedBlock: DocBlock | undefined;
/** All @see blocks */
readonly seeBlocks: ReadonlyArray<DocBlock>;
/** All @example blocks */
readonly exampleBlocks: ReadonlyArray<DocBlock>;
/** Custom blocks not handled by standard properties */
readonly customBlocks: ReadonlyArray<DocBlock>;
/** Modifier tags like @public, @beta, @alpha */
readonly modifierTagSet: StandardModifierTagSet;
}Usage Examples:
import { TSDocParser } from "@microsoft/tsdoc";
const parser = new TSDocParser();
const context = parser.parseString(`
/**
* Calculates something important.
* @param value - The input value
* @returns The calculated result
* @beta
*/
`);
const comment = context.docComment;
console.log(comment.summarySection); // "Calculates something important."
console.log(comment.parameterBlocks.length); // 1
console.log(comment.modifierTagSet.isBeta); // trueContainer nodes for organizing content within comments.
/**
* Container for the main content of a comment section
*/
class DocSection extends DocNodeContainer {
// Inherits appendNode() and getChildNodes() from DocNodeContainer
}
/**
* Represents a paragraph of content
*/
class DocParagraph extends DocNodeContainer {
// Inherits appendNode() and getChildNodes() from DocNodeContainer
}
/**
* Base class for nodes that contain other nodes
*/
abstract class DocNodeContainer extends DocNode {
/** Get all child nodes as an array */
getChildNodes(): ReadonlyArray<DocNode>;
/** Add a child node to this container */
appendNode(docNode: DocNode): void;
/** Add multiple child nodes to this container */
appendNodes(docNodes: ReadonlyArray<DocNode>): void;
}Represents block-level tags like @remarks, @example, @param.
/**
* Represents a block-level tag like @remarks or @example
*/
class DocBlock extends DocNode {
/** The tag portion (e.g., "@remarks") */
readonly blockTag: DocBlockTag;
/** The content following the tag */
readonly content: DocSection;
}
/**
* Represents the tag portion of a block tag
*/
class DocBlockTag extends DocNode {
/** The tag name without the @ symbol */
readonly tagName: string;
/** The tag name in uppercase for case-insensitive comparison */
readonly tagNameWithUpperCase: string;
}Specialized block for @param and @typeParam tags.
/**
* Specialized block for @param and @typeParam tags
*/
class DocParamBlock extends DocBlock {
/** The parameter name being documented */
readonly parameterName: string;
}
/**
* Collection of parameter blocks with lookup methods
*/
class DocParamCollection extends DocNode {
/** All parameter blocks in the collection */
readonly blocks: ReadonlyArray<DocParamBlock>;
/** Find a parameter block by name */
tryGetBlockByName(parameterName: string): DocParamBlock | undefined;
}Usage Examples:
import { TSDocParser } from "@microsoft/tsdoc";
const parser = new TSDocParser();
const context = parser.parseString(`
/**
* @param name - The user's name
* @param age - The user's age
* @returns User information
*/
`);
const comment = context.docComment;
// Access parameter blocks
comment.parameterBlocks.forEach(paramBlock => {
console.log(`Parameter: ${paramBlock.parameterName}`);
console.log(`Tag: ${paramBlock.blockTag.tagName}`);
});
// Find specific parameter
const nameParam = comment.parameterBlocks.find(
p => p.parameterName === "name"
);Nodes representing different types of text content.
/**
* Plain text content within a comment
*/
class DocPlainText extends DocNode {
/** The plain text content */
readonly text: string;
}
/**
* Inline code span (backtick-delimited)
*/
class DocCodeSpan extends DocNode {
/** The code content without the backticks */
readonly code: string;
}
/**
* Fenced code block with language specification
*/
class DocFencedCode extends DocNode {
/** Programming language for syntax highlighting */
readonly language: string;
/** The code content without the fence markers */
readonly code: string;
}
/**
* Escaped text (backslash escapes)
*/
class DocEscapedText extends DocNode {
/** The original encoded text with backslashes */
readonly encodedText: string;
/** The decoded text with escapes resolved */
readonly decodedText: string;
}
/**
* Text that could not be parsed correctly
*/
class DocErrorText extends DocNode {
/** The problematic text */
readonly text: string;
/** Error message ID */
readonly messageId: TSDocMessageId;
/** Human-readable error message */
readonly errorMessage: string;
/** Location of the error */
readonly errorLocation: TextRange;
}
/**
* Soft line break (newline that doesn't end a paragraph)
*/
class DocSoftBreak extends DocNode {
// No additional properties - represents a soft break
}Nodes representing inline tags like {@link} and {@inheritDoc}.
/**
* Base class for inline tags
*/
abstract class DocInlineTagBase extends DocNode {
/** The tag name without braces (e.g., "link", "inheritDoc") */
readonly tagName: string;
/** The tag name in uppercase for comparison */
readonly tagNameWithUpperCase: string;
}
/**
* Inline {@link} tag for cross-references
*/
class DocLinkTag extends DocInlineTagBase {
/** Code destination for API references */
readonly codeDestination: DocCodeSpan | undefined;
/** URL destination for external links */
readonly urlDestination: string | undefined;
/** Display text for the link */
readonly linkText: string | undefined;
}
/**
* Generic inline tag like {@label} or custom tags
*/
class DocInlineTag extends DocInlineTagBase {
/** The content inside the tag braces */
readonly tagContent: string;
}
/**
* Special {@inheritDoc} tag
*/
class DocInheritDocTag extends DocInlineTagBase {
/** Optional declaration reference for selective inheritance */
readonly declarationReference: DocDeclarationReference | undefined;
}Nodes for referencing API declarations in {@link} and {@inheritDoc} tags.
/**
* Reference to an API declaration
*/
class DocDeclarationReference extends DocNode {
/** Optional package name */
readonly packageName: string | undefined;
/** Optional import path */
readonly importPath: string | undefined;
/** Chain of member references */
readonly memberReferences: ReadonlyArray<DocMemberReference>;
}
/**
* Reference to a class member or namespace member
*/
class DocMemberReference extends DocNode {
/** Member symbol (., #, ~) indicating access level */
readonly memberSymbol: DocMemberSymbol | undefined;
/** Member identifier (name) */
readonly memberIdentifier: DocMemberIdentifier | undefined;
/** Selector for overloaded members */
readonly selector: DocMemberSelector | undefined;
}
/**
* Identifier part of a member reference
*/
class DocMemberIdentifier extends DocNode {
/** The identifier name */
readonly identifier: string;
}
/**
* Symbol notation in member references
*/
class DocMemberSymbol extends DocNode {
/** The symbol text (., #, or ~) */
readonly symbolText: string;
}
/**
* Selector for overloaded members
*/
class DocMemberSelector extends DocNode {
/** The selector text */
readonly selector: string;
}Nodes representing HTML tags within comments.
/**
* HTML start tag like <b> or <a href="...">
*/
class DocHtmlStartTag extends DocNode {
/** Tag name (e.g., "b", "a") */
readonly name: string;
/** Spacing after the tag name */
readonly spacingAfterName: string | undefined;
/** HTML attributes */
readonly htmlAttributes: ReadonlyArray<DocHtmlAttribute>;
/** Whether this is a self-closing tag */
readonly selfClosingTag: boolean;
}
/**
* HTML end tag like </b>
*/
class DocHtmlEndTag extends DocNode {
/** Tag name (e.g., "b", "a") */
readonly name: string;
}
/**
* HTML attribute within an HTML tag
*/
class DocHtmlAttribute extends DocNode {
/** Attribute name */
readonly name: string;
/** Attribute value (without quotes) */
readonly value: string | undefined;
/** Spacing after attribute name */
readonly spacingAfterName: string | undefined;
/** Spacing before equals sign */
readonly spacingBeforeEquals: string | undefined;
/** Spacing after equals sign */
readonly spacingAfterEquals: string | undefined;
}Special node for token-level content representation.
/**
* Excerpt of tokens representing part of the input
*/
class DocExcerpt extends DocNode {
/** The type of excerpt */
readonly excerptKind: ExcerptKind;
/** Token sequence for this excerpt */
readonly content: TokenSequence;
}
enum ExcerptKind {
Content = "Content",
Spacing = "Spacing",
BlockTag = "BlockTag",
InlineTag = "InlineTag"
}Usage Examples:
import { TSDocParser } from "@microsoft/tsdoc";
const parser = new TSDocParser();
const context = parser.parseString(`
/**
* See {@link MyClass.method} for details.
*
* \`\`\`typescript
* const result = calculate(42);
* \`\`\`
*
* @example
* Simple usage:
* \`const x = foo();\`
*/
`);
// Find inline links
const findLinks = (node: DocNode): DocLinkTag[] => {
const links: DocLinkTag[] = [];
if (node instanceof DocLinkTag) {
links.push(node);
}
for (const child of node.getChildNodes()) {
if (child) {
links.push(...findLinks(child));
}
}
return links;
};
const links = findLinks(context.docComment);
console.log(`Found ${links.length} links`);
// Find code blocks
const findCodeBlocks = (node: DocNode): DocFencedCode[] => {
const blocks: DocFencedCode[] = [];
if (node instanceof DocFencedCode) {
blocks.push(node);
}
for (const child of node.getChildNodes()) {
if (child) {
blocks.push(...findCodeBlocks(child));
}
}
return blocks;
};
const codeBlocks = findCodeBlocks(context.docComment);
codeBlocks.forEach(block => {
console.log(`Code block (${block.language}): ${block.code}`);
});