in tsdoc/src/parser/NodeParser.ts [1829:1921]
private _parseHtmlStartTag(tokenReader: TokenReader): DocNode {
tokenReader.assertAccumulatedSequenceIsEmpty();
const marker: number = tokenReader.createMarker();
// Read the "<" delimiter
const lessThanToken: Token = tokenReader.readToken();
if (lessThanToken.kind !== TokenKind.LessThan) {
// This would be a parser bug -- the caller of _parseHtmlStartTag() should have verified this while
// looking ahead
throw new Error('Expecting an HTML tag starting with "<"');
}
// NOTE: CommonMark does not permit whitespace after the "<"
const openingDelimiterExcerpt: TokenSequence = tokenReader.extractAccumulatedSequence();
// Read the element name
const nameExcerpt: ResultOrFailure<TokenSequence> = this._parseHtmlName(tokenReader);
if (isFailure(nameExcerpt)) {
return this._backtrackAndCreateErrorForFailure(
tokenReader,
marker,
'Invalid HTML element: ',
nameExcerpt
);
}
const spacingAfterNameExcerpt: TokenSequence | undefined = this._tryReadSpacingAndNewlines(tokenReader);
const htmlAttributes: DocHtmlAttribute[] = [];
// Read the attributes until we see a ">" or "/>"
while (tokenReader.peekTokenKind() === TokenKind.AsciiWord) {
// Read the attribute
const attributeNode: ResultOrFailure<DocHtmlAttribute> = this._parseHtmlAttribute(tokenReader);
if (isFailure(attributeNode)) {
return this._backtrackAndCreateErrorForFailure(
tokenReader,
marker,
'The HTML element has an invalid attribute: ',
attributeNode
);
}
htmlAttributes.push(attributeNode);
}
// Read the closing "/>" or ">" as the Excerpt.suffix
tokenReader.assertAccumulatedSequenceIsEmpty();
const endDelimiterMarker: number = tokenReader.createMarker();
let selfClosingTag: boolean = false;
if (tokenReader.peekTokenKind() === TokenKind.Slash) {
tokenReader.readToken();
selfClosingTag = true;
}
if (tokenReader.peekTokenKind() !== TokenKind.GreaterThan) {
const failure: IFailure = this._createFailureForTokensSince(
tokenReader,
TSDocMessageId.HtmlTagMissingGreaterThan,
'Expecting an attribute or ">" or "/>"',
endDelimiterMarker
);
return this._backtrackAndCreateErrorForFailure(
tokenReader,
marker,
'The HTML tag has invalid syntax: ',
failure
);
}
tokenReader.readToken();
const closingDelimiterExcerpt: TokenSequence = tokenReader.extractAccumulatedSequence();
// NOTE: We don't read excerptParameters.separator here, since if there is any it
// will be represented as DocPlainText.
return new DocHtmlStartTag({
parsed: true,
configuration: this._configuration,
openingDelimiterExcerpt,
nameExcerpt,
spacingAfterNameExcerpt,
htmlAttributes,
selfClosingTag,
closingDelimiterExcerpt
});
}