@@ -21,6 +21,27 @@ import {
21
21
UnionTypeNode
22
22
} from 'typescript' ;
23
23
import { isDynamicallyAdded } from './plugin-utils' ;
24
+ import {
25
+ DocNode ,
26
+ DocExcerpt ,
27
+ TSDocParser ,
28
+ ParserContext ,
29
+ DocComment ,
30
+ DocBlock
31
+ } from '@microsoft/tsdoc' ;
32
+
33
+ export function renderDocNode ( docNode : DocNode ) {
34
+ let result : string = '' ;
35
+ if ( docNode ) {
36
+ if ( docNode instanceof DocExcerpt ) {
37
+ result += docNode . content . toString ( ) ;
38
+ }
39
+ for ( const childNode of docNode . getChildNodes ( ) ) {
40
+ result += renderDocNode ( childNode ) ;
41
+ }
42
+ }
43
+ return result ;
44
+ }
24
45
25
46
export function isArray ( type : Type ) {
26
47
const symbol = type . getSymbol ( ) ;
@@ -121,114 +142,89 @@ export function getMainCommentOfNode(
121
142
node : Node ,
122
143
sourceFile : SourceFile
123
144
) : string {
124
- const sourceText = sourceFile . getFullText ( ) ;
125
- // in case we decide to include "// comments"
126
- const replaceRegex =
127
- / ^ \s * \* * * @ .* $ | ^ \s * \/ \* + * | ^ \s * \/ \/ + .* | ^ \s * \/ + * | ^ \s * \* + * | + $ | * \* * \/ * $ / gim;
128
- //const replaceRegex = /^ *\** *@.*$|^ *\/\*+ *|^ *\/+ *|^ *\*+ *| +$| *\**\/ *$/gim;
129
-
130
- const commentResult = [ ] ;
131
- const introspectComments = ( comments ?: CommentRange [ ] ) =>
132
- comments ?. forEach ( ( comment ) => {
133
- const commentSource = sourceText . substring ( comment . pos , comment . end ) ;
134
- const oneComment = commentSource . replace ( replaceRegex , '' ) . trim ( ) ;
135
- if ( oneComment ) {
136
- commentResult . push ( oneComment ) ;
137
- }
138
- } ) ;
139
-
140
- const leadingCommentRanges = getLeadingCommentRanges (
141
- sourceText ,
142
- node . getFullStart ( )
145
+ const tsdocParser : TSDocParser = new TSDocParser ( ) ;
146
+ const parserContext : ParserContext = tsdocParser . parseString (
147
+ node . getFullText ( )
143
148
) ;
144
- introspectComments ( leadingCommentRanges ) ;
145
- if ( ! commentResult . length ) {
146
- const trailingCommentRanges = getTrailingCommentRanges (
147
- sourceText ,
148
- node . getFullStart ( )
149
- ) ;
150
- introspectComments ( trailingCommentRanges ) ;
149
+ const docComment : DocComment = parserContext . docComment ;
150
+ return renderDocNode ( docComment . summarySection ) . trim ( ) ;
151
+ }
152
+
153
+ export function parseCommentDocValue ( docValue : string , type : ts . Type ) {
154
+ let value = docValue . replace ( / ' / g, '"' ) . trim ( ) ;
155
+
156
+ if ( ! type || ! isString ( type ) ) {
157
+ try {
158
+ value = JSON . parse ( value ) ;
159
+ } catch { }
160
+ } else if ( isString ( type ) ) {
161
+ if ( value . split ( ' ' ) . length !== 1 && ! value . startsWith ( '"' ) ) {
162
+ value = null ;
163
+ } else {
164
+ value = value . replace ( / " / g, '' ) ;
165
+ }
151
166
}
152
- return commentResult . join ( '\n' ) ;
167
+ return value ;
153
168
}
154
169
155
- export function getTsDocTagsOfNode (
156
- node : Node ,
157
- sourceFile : SourceFile ,
158
- typeChecker : TypeChecker
159
- ) {
160
- const sourceText = sourceFile . getFullText ( ) ;
170
+ export function getTsDocTagsOfNode ( node : Node , typeChecker : TypeChecker ) {
171
+ const tsdocParser : TSDocParser = new TSDocParser ( ) ;
172
+ const parserContext : ParserContext = tsdocParser . parseString (
173
+ node . getFullText ( )
174
+ ) ;
175
+ const docComment : DocComment = parserContext . docComment ;
161
176
162
177
const tagDefinitions : {
163
178
[ key : string ] : {
164
- regex : RegExp ;
165
179
hasProperties : boolean ;
166
180
repeatable : boolean ;
167
181
} ;
168
182
} = {
169
183
example : {
170
- regex :
171
- / @ e x a m p l e * ( ( [ ' " ] (?< string > .+ ?) [ ' " ] ) | (?< booleanOrNumber > [ ^ ] + ?) | (?< array > ( \[ .+ ?\] ) ) ) * $ / gim,
172
184
hasProperties : true ,
173
185
repeatable : true
174
- } ,
175
- deprecated : {
176
- regex : / @ d e p r e c a t e d * / gim,
177
- hasProperties : false ,
178
- repeatable : false
179
186
}
180
187
} ;
181
188
182
189
const tagResults : any = { } ;
183
- const introspectTsDocTags = ( comments ?: CommentRange [ ] ) =>
184
- comments ?. forEach ( ( comment ) => {
185
- const commentSource = sourceText . substring ( comment . pos , comment . end ) ;
186
-
187
- for ( const tag in tagDefinitions ) {
188
- const { regex, hasProperties, repeatable } = tagDefinitions [ tag ] ;
189
-
190
- let value : any ;
191
-
192
- let execResult : RegExpExecArray ;
193
- while (
194
- ( execResult = regex . exec ( commentSource ) ) &&
195
- ( ! hasProperties || execResult . length > 1 )
196
- ) {
197
- if ( repeatable && ! tagResults [ tag ] ) tagResults [ tag ] = [ ] ;
198
-
199
- if ( hasProperties ) {
200
- const docValue =
201
- execResult . groups ?. string ??
202
- execResult . groups ?. booleanOrNumber ??
203
- ( execResult . groups ?. array &&
204
- execResult . groups . array . replace ( / ' / g, '"' ) ) ;
205
-
206
- const type = typeChecker . getTypeAtLocation ( node ) ;
207
-
208
- value = docValue ;
209
- if ( ! type || ! isString ( type ) ) {
210
- try {
211
- value = JSON . parse ( value ) ;
212
- } catch { }
213
- }
214
- } else {
215
- value = true ;
216
- }
217
190
218
- if ( repeatable ) {
219
- tagResults [ tag ] . push ( value ) ;
220
- } else {
221
- tagResults [ tag ] = value ;
191
+ const introspectTsDocTags = ( docComment : DocComment ) => {
192
+ for ( const tag in tagDefinitions ) {
193
+ const { hasProperties, repeatable } = tagDefinitions [ tag ] ;
194
+ const blocks = docComment . customBlocks . filter (
195
+ ( block ) => block . blockTag . tagName === `@${ tag } `
196
+ ) ;
197
+ if ( blocks . length === 0 ) continue ;
198
+ if ( repeatable && ! tagResults [ tag ] ) tagResults [ tag ] = [ ] ;
199
+ const type = typeChecker . getTypeAtLocation ( node ) ;
200
+ if ( hasProperties ) {
201
+ blocks . forEach ( ( block ) => {
202
+ const docValue = renderDocNode ( block . content ) . split ( '\n' ) [ 0 ] ;
203
+ const value = parseCommentDocValue ( docValue , type ) ;
204
+
205
+ if ( value !== null ) {
206
+ if ( repeatable ) {
207
+ tagResults [ tag ] . push ( value ) ;
208
+ } else {
209
+ tagResults [ tag ] = value ;
210
+ }
222
211
}
223
- }
212
+ } ) ;
213
+ } else {
214
+ tagResults [ tag ] = true ;
224
215
}
225
- } ) ;
216
+ }
217
+ if ( docComment . remarksBlock ) {
218
+ tagResults [ 'remarks' ] = renderDocNode (
219
+ docComment . remarksBlock . content
220
+ ) . trim ( ) ;
221
+ }
222
+ if ( docComment . deprecatedBlock ) {
223
+ tagResults [ 'deprecated' ] = true ;
224
+ }
225
+ } ;
226
+ introspectTsDocTags ( docComment ) ;
226
227
227
- const leadingCommentRanges = getLeadingCommentRanges (
228
- sourceText ,
229
- node . getFullStart ( )
230
- ) ;
231
- introspectTsDocTags ( leadingCommentRanges ) ;
232
228
return tagResults ;
233
229
}
234
230