88 * @typedef {import("eslint").Rule.RuleModule } RuleModule
99 * @typedef {import('@microsoft/tsdoc').DocNode } DocNode
1010 * @typedef {import('@microsoft/tsdoc').DocPlainText } DocPlainText
11+ *
12+ * @typedef {{
13+ * startIndex: number; // Starting character index of the hyphen pattern (inclusive).
14+ * endIndex: number; // Ending character index of the hyphen pattern (exclusive).
15+ * }} HyphenPatternMatch
1116 */
1217
18+ const { fail } = require ( "node:assert" ) ;
1319const { DocNodeKind, TSDocParser } = require ( "@microsoft/tsdoc" ) ;
1420
1521const parser = new TSDocParser ( ) ;
1622
1723/**
1824 * Checks if a comment text starts with a hyphen.
1925 * @param {DocPlainText } plainTextNode - The plain text node to check.
26+ * @return {HyphenPatternMatch | undefined } The hyphen pattern match info if found; otherwise, undefined.
2027 */
2128function doesTextNodeStartWithHyphen ( plainTextNode ) {
22- return plainTextNode . text . trimStart ( ) . startsWith ( "-" ) ;
29+ // RegEx explanation:
30+ // ^\s* - Match the start of the string, followed by zero or more whitespace characters
31+ // - - Match the `-` character literal
32+ // \s* - Match zero or more whitespace characters
33+ const match = plainTextNode . text . match ( / ^ \s * - \s * / ) ;
34+
35+ if ( ! match ) {
36+ return undefined ;
37+ }
38+
39+ const textRange =
40+ plainTextNode . textExcerpt ?. getContainingTextRange ( ) ??
41+ fail ( "Expected textExcerpt to be defined." ) ;
42+ return {
43+ startIndex : textRange . pos ,
44+ endIndex : textRange . pos + match [ 0 ] . length ,
45+ } ;
2346}
2447
2548/**
2649 * Checks if a comment body starts with a hyphen.
2750 * @param { DocNode } commentBodyNode - The doc node representing the body of the comment.
51+ * @return {HyphenPatternMatch | undefined } The hyphen pattern match info if found; otherwise, undefined.
2852 */
2953function doesCommentBodyStartWithHyphen ( commentBodyNode ) {
3054 // Walk down first node of the tree until we find a leaf.
@@ -36,7 +60,7 @@ function doesCommentBodyStartWithHyphen(commentBodyNode) {
3660
3761 const childNodes = commentBodyNode . getChildNodes ( ) ;
3862 if ( childNodes . length === 0 ) {
39- return false ;
63+ return undefined ;
4064 }
4165
4266 return doesCommentBodyStartWithHyphen ( childNodes [ 0 ] ) ;
@@ -58,6 +82,7 @@ const rule = {
5882 hyphenAfterTag :
5983 "JSDoc/TSDoc block tags must not be followed by a hyphen character (`-`)." ,
6084 } ,
85+ fixable : "code" ,
6186 schema : [ ] ,
6287 } ,
6388
@@ -101,25 +126,20 @@ const rule = {
101126 // Note: the TSDoc format makes it difficult to extract the range information for the block content specifically.
102127 // Instead, we just report the range for the tag itself.
103128 for ( const block of blocksToCheck ) {
104- if ( doesCommentBodyStartWithHyphen ( block . content ) ) {
105- const tagTextRange = block . blockTag
106- . getTokenSequence ( )
107- . getContainingTextRange ( ) ;
108- const tagTextRangeStart = tagTextRange . pos - 1 ; // Include the `@`
109- const tagTextRangeEnd = tagTextRange . end ;
110- const startIndex = sourceCode . getLocFromIndex (
111- commentStartIndex + tagTextRangeStart ,
112- ) ;
113- const endIndex = sourceCode . getLocFromIndex (
114- commentStartIndex + tagTextRangeEnd ,
115- ) ;
129+ const hyphenMatch = doesCommentBodyStartWithHyphen ( block . content ) ;
130+ if ( hyphenMatch ) {
131+ const startIndex = commentStartIndex + hyphenMatch . startIndex - 1 ;
132+ const endIndex = commentStartIndex + hyphenMatch . endIndex - 1 ;
116133
117134 context . report ( {
118135 loc : {
119- start : startIndex ,
120- end : endIndex ,
136+ start : sourceCode . getLocFromIndex ( startIndex ) ,
137+ end : sourceCode . getLocFromIndex ( endIndex ) ,
121138 } ,
122139 messageId : "hyphenAfterTag" ,
140+ fix ( fixer ) {
141+ return fixer . replaceTextRange ( [ startIndex , endIndex ] , " " ) ;
142+ } ,
123143 } ) ;
124144 }
125145 }
0 commit comments