@@ -85,8 +85,24 @@ function parseDocParts(comments: string | undefined, tags: ts.JSDocTagInfo[]): D
8585 docs . see = eatTag ( 'see' ) ;
8686 docs . subclassable = eatTag ( 'subclassable' ) !== undefined ? true : undefined ;
8787
88+ docs . stability = parseStability ( eatTag ( 'stability' ) , diagnostics ) ;
89+ // @experimental is a shorthand for '@stability experimental', same for '@stable'
8890 const experimental = eatTag ( 'experimental' ) !== undefined ;
8991 const stable = eatTag ( 'stable' ) !== undefined ;
92+ // Can't combine them
93+ if ( countBools ( docs . stability !== undefined , experimental , stable ) > 1 ) {
94+ diagnostics . push ( `Use only one of @stability, @experimental or @stable` ) ;
95+ }
96+ if ( experimental ) { docs . stability = spec . Stability . Experimental ; }
97+ if ( stable ) { docs . stability = spec . Stability . Stable ; }
98+
99+ // Can combine '@stability deprecated' with '@deprecated <reason>'
100+ if ( docs . deprecated !== undefined ) {
101+ if ( docs . stability !== undefined && docs . stability !== spec . Stability . Deprecated ) {
102+ diagnostics . push ( `@deprecated tag requires '@stability deprecated' or no @stability at all.` ) ;
103+ }
104+ docs . stability = spec . Stability . Deprecated ;
105+ }
90106
91107 if ( docs . example && docs . example . indexOf ( '```' ) >= 0 ) {
92108 // This is currently what the JSDoc standard expects, and VSCode highlights it in
@@ -97,26 +113,10 @@ function parseDocParts(comments: string | undefined, tags: ts.JSDocTagInfo[]): D
97113 diagnostics . push ( '@example must be code only, no code block fences allowed.' ) ;
98114 }
99115
100- if ( experimental && stable ) {
101- diagnostics . push ( 'Element is marked both @experimental and @stable.' ) ;
102- }
103-
104- if ( docs . deprecated !== undefined ) {
105- if ( docs . deprecated . trim ( ) === '' ) {
106- diagnostics . push ( '@deprecated tag needs a reason and/or suggested alternatives.' ) ;
107- }
108- if ( stable ) {
109- diagnostics . push ( 'Element is marked both @deprecated and @stable.' ) ;
110- }
111- if ( experimental ) {
112- diagnostics . push ( 'Element is marked both @deprecated and @experimental.' ) ;
113- }
116+ if ( docs . deprecated !== undefined && docs . deprecated . trim ( ) === '' ) {
117+ diagnostics . push ( '@deprecated tag needs a reason and/or suggested alternatives.' ) ;
114118 }
115119
116- if ( experimental ) { docs . stability = spec . Stability . Experimental ; }
117- if ( stable ) { docs . stability = spec . Stability . Stable ; }
118- if ( docs . deprecated ) { docs . stability = spec . Stability . Deprecated ; }
119-
120120 if ( tagNames . size > 0 ) {
121121 docs . custom = { } ;
122122 for ( const [ key , value ] of tagNames . entries ( ) ) {
@@ -184,3 +184,24 @@ function summaryLine(str: string) {
184184const PUNCTUATION = [ '!' , '?' , '.' , ';' ] . map ( s => '\\' + s ) . join ( '' ) ;
185185const ENDS_WITH_PUNCTUATION_REGEX = new RegExp ( `[${ PUNCTUATION } ]$` ) ;
186186const FIRST_SENTENCE_REGEX = new RegExp ( `^([^${ PUNCTUATION } ]+[${ PUNCTUATION } ] )` ) ; // literal space at the end
187+
188+ function intBool ( x : boolean ) : number {
189+ return x ? 1 : 0 ;
190+ }
191+
192+ function countBools ( ...x : boolean [ ] ) {
193+ return x . map ( intBool ) . reduce ( ( a , b ) => a + b , 0 ) ;
194+ }
195+
196+ function parseStability ( s : string | undefined , diagnostics : string [ ] ) : spec . Stability | undefined {
197+ if ( s === undefined ) { return undefined ; }
198+
199+ switch ( s ) {
200+ case 'stable' : return spec . Stability . Stable ;
201+ case 'experimental' : return spec . Stability . Experimental ;
202+ case 'external' : return spec . Stability . External ;
203+ case 'deprecated' : return spec . Stability . Deprecated ;
204+ }
205+ diagnostics . push ( `Unrecognized @stability: '${ s } '` ) ;
206+ return undefined ;
207+ }
0 commit comments