@@ -12,14 +12,17 @@ import {
12
12
TypeChecker ,
13
13
TypeFlags ,
14
14
TypeFormatFlags ,
15
- SourceFile ,
16
- CommentRange ,
17
- getLeadingCommentRanges ,
18
- getTrailingCommentRanges ,
19
15
UnionTypeNode ,
20
16
TypeNode ,
17
+ JSDoc ,
18
+ getTextOfJSDocComment ,
19
+ getJSDocDeprecatedTag ,
20
+ ModifiersArray ,
21
+ NodeArray ,
22
+ getJSDocTags ,
21
23
} from 'typescript' ;
22
24
import { isDynamicallyAdded } from './plugin-utils' ;
25
+ import * as ts from 'typescript' ;
23
26
24
27
export function isArray ( type : Type ) {
25
28
const symbol = type . getSymbol ( ) ;
@@ -144,7 +147,7 @@ export function getDecoratorName(decorator: Decorator) {
144
147
145
148
function getIdentifierFromName ( expression : LeftHandSideExpression ) {
146
149
const identifier = getNameFromExpression ( expression ) ;
147
- if ( identifier && identifier . kind !== SyntaxKind . Identifier ) {
150
+ if ( expression && expression . kind !== SyntaxKind . Identifier ) {
148
151
throw new Error ( ) ;
149
152
}
150
153
return identifier ;
@@ -157,39 +160,28 @@ function getNameFromExpression(expression: LeftHandSideExpression) {
157
160
return expression ;
158
161
}
159
162
160
- export function getDescriptionOfNode (
161
- node : Node ,
162
- sourceFile : SourceFile ,
163
- ) : string {
164
- const sourceText = sourceFile . getFullText ( ) ;
165
- // in case we decide to include "// comments"
166
- const replaceRegex =
167
- / ^ * \* * * @ .* $ | ^ * \/ \* + * | ^ * \/ \/ + .* | ^ * \/ + * | ^ * \* + * | + $ | * \* * \/ * $ / gim;
168
- //const replaceRegex = /^ *\** *@.*$|^ *\/\*+ *|^ *\/+ *|^ *\*+ *| +$| *\**\/ *$/gim;
169
-
170
- const description = [ ] ;
171
- const introspectCommentsAndExamples = ( comments ?: CommentRange [ ] ) =>
172
- comments ?. forEach ( ( comment ) => {
173
- const commentSource = sourceText . substring ( comment . pos , comment . end ) ;
174
- const oneComment = commentSource . replace ( replaceRegex , '' ) . trim ( ) ;
175
- if ( oneComment ) {
176
- description . push ( oneComment ) ;
177
- }
178
- } ) ;
163
+ export function getJSDocDescription ( node : Node ) : string {
164
+ const jsDoc : JSDoc [ ] = ( node as any ) . jsDoc ;
179
165
180
- const leadingCommentRanges = getLeadingCommentRanges (
181
- sourceText ,
182
- node . getFullStart ( ) ,
183
- ) ;
184
- introspectCommentsAndExamples ( leadingCommentRanges ) ;
185
- if ( ! description . length ) {
186
- const trailingCommentRanges = getTrailingCommentRanges (
187
- sourceText ,
188
- node . getFullStart ( ) ,
189
- ) ;
190
- introspectCommentsAndExamples ( trailingCommentRanges ) ;
166
+ if ( ! jsDoc ) {
167
+ return undefined ;
168
+ }
169
+
170
+ return getTextOfJSDocComment ( jsDoc [ 0 ] . comment ) ;
171
+ }
172
+
173
+ export function hasJSDocTags ( node : Node , tagName : string [ ] ) : boolean {
174
+ const tags = getJSDocTags ( node ) ;
175
+ return tags . some ( ( tag ) => tagName . includes ( tag . tagName . text ) ) ;
176
+ // return jsDoc;
177
+ }
178
+
179
+ export function getJsDocDeprecation ( node : Node ) : string {
180
+ const deprecatedTag = getJSDocDeprecatedTag ( node ) ;
181
+ if ( ! deprecatedTag ) {
182
+ return undefined ;
191
183
}
192
- return description . join ( '\n' ) ;
184
+ return getTextOfJSDocComment ( deprecatedTag . comment ) || 'deprecated' ;
193
185
}
194
186
195
187
export function findNullableTypeFromUnion (
@@ -200,3 +192,201 @@ export function findNullableTypeFromUnion(
200
192
hasFlag ( typeChecker . getTypeAtLocation ( tNode ) , TypeFlags . Null ) ,
201
193
) ;
202
194
}
195
+
196
+ export function hasModifiers (
197
+ modifiers : ModifiersArray ,
198
+ toCheck : SyntaxKind [ ] ,
199
+ ) : boolean {
200
+ if ( ! modifiers ) {
201
+ return false ;
202
+ }
203
+ return modifiers . some ( ( modifier ) => toCheck . includes ( modifier . kind ) ) ;
204
+ }
205
+
206
+ export function hasDecorators (
207
+ decorators : NodeArray < Decorator > ,
208
+ toCheck : string [ ] ,
209
+ ) : boolean {
210
+ if ( ! decorators ) {
211
+ return false ;
212
+ }
213
+
214
+ return decorators . some ( ( decorator ) => {
215
+ return toCheck . includes ( getDecoratorName ( decorator ) ) ;
216
+ } ) ;
217
+ }
218
+
219
+ export function hasImport ( sf : ts . SourceFile , what : string ) : boolean {
220
+ for ( const statement of sf . statements ) {
221
+ if (
222
+ ts . isImportDeclaration ( statement ) &&
223
+ ts . isNamedImports ( statement . importClause . namedBindings )
224
+ ) {
225
+ const bindings = statement . importClause . namedBindings . elements ;
226
+
227
+ for ( const namedBinding of bindings ) {
228
+ if ( namedBinding . name . text === what ) {
229
+ return true ;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ return false ;
235
+ }
236
+
237
+ export function createImportEquals (
238
+ f : ts . NodeFactory ,
239
+ identifier : ts . Identifier | string ,
240
+ from : string ,
241
+ ) : ts . ImportEqualsDeclaration {
242
+ const [ major , minor ] = ts . versionMajorMinor ?. split ( '.' ) . map ( ( x ) => + x ) ;
243
+
244
+ if ( major == 4 && minor >= 2 ) {
245
+ // support TS v4.2+
246
+ return f . createImportEqualsDeclaration (
247
+ undefined ,
248
+ undefined ,
249
+ false ,
250
+ identifier ,
251
+ f . createExternalModuleReference ( f . createStringLiteral ( from ) ) ,
252
+ ) ;
253
+ }
254
+ return ( f . createImportEqualsDeclaration as any ) (
255
+ undefined ,
256
+ undefined ,
257
+ identifier ,
258
+ f . createExternalModuleReference ( f . createStringLiteral ( from ) ) ,
259
+ ) ;
260
+ }
261
+
262
+ export function createNamedImport (
263
+ f : ts . NodeFactory ,
264
+ what : string [ ] ,
265
+ from : string ,
266
+ ) {
267
+ return f . createImportDeclaration (
268
+ undefined ,
269
+ undefined ,
270
+ f . createImportClause (
271
+ false ,
272
+ undefined ,
273
+ f . createNamedImports (
274
+ what . map ( ( name ) =>
275
+ f . createImportSpecifier ( false , undefined , f . createIdentifier ( name ) ) ,
276
+ ) ,
277
+ ) ,
278
+ ) ,
279
+ f . createStringLiteral ( from ) ,
280
+ ) ;
281
+ }
282
+
283
+ export function isCallExpressionOf ( name : string , node : ts . CallExpression ) {
284
+ return ts . isIdentifier ( node . expression ) && node . expression . text === name ;
285
+ }
286
+
287
+ export type PrimitiveObject = {
288
+ [ key : string ] : string | boolean | ts . Node | PrimitiveObject ;
289
+ } ;
290
+
291
+ function isNode ( value : any ) : value is ts . Node {
292
+ return typeof value === 'object' && value . constructor . name === 'NodeObject' ;
293
+ }
294
+
295
+ export function serializePrimitiveObjectToAst (
296
+ f : ts . NodeFactory ,
297
+ object : PrimitiveObject ,
298
+ ) : ts . ObjectLiteralExpression {
299
+ const properties = [ ] ;
300
+
301
+ Object . keys ( object ) . forEach ( ( key ) => {
302
+ const value = object [ key ] ;
303
+
304
+ if ( value === undefined ) {
305
+ return ;
306
+ }
307
+
308
+ let initializer : ts . Expression ;
309
+ if ( isNode ( value ) ) {
310
+ initializer = value as ts . Expression ;
311
+ } else if ( typeof value === 'string' ) {
312
+ initializer = f . createStringLiteral ( value ) ;
313
+ } else if ( typeof value === 'boolean' ) {
314
+ initializer = value ? f . createTrue ( ) : f . createFalse ( ) ;
315
+ } else if ( typeof value === 'object' ) {
316
+ initializer = serializePrimitiveObjectToAst ( f , value ) ;
317
+ }
318
+
319
+ properties . push ( f . createPropertyAssignment ( key , initializer ) ) ;
320
+ } ) ;
321
+
322
+ return f . createObjectLiteralExpression ( properties ) ;
323
+ }
324
+
325
+ export function safelyMergeObjects (
326
+ f : ts . NodeFactory ,
327
+ a : ts . Expression ,
328
+ b : ts . Expression ,
329
+ ) {
330
+ // if both of objects are ObjectLiterals, so merge property by property in compile time
331
+ // if one or both of expressions not an object literal, produce rest spread and merge in runtime
332
+ if ( ts . isObjectLiteralExpression ( a ) && ts . isObjectLiteralExpression ( b ) ) {
333
+ const aMap = a . properties . reduce ( ( acc , prop ) => {
334
+ acc [ ( prop . name as ts . Identifier ) . text ] = prop ;
335
+ return acc ;
336
+ } , { } as { [ propName : string ] : ts . ObjectLiteralElementLike } ) ;
337
+
338
+ b . properties . forEach ( ( prop ) => {
339
+ aMap [ ( prop . name as ts . Identifier ) . text ] = prop ;
340
+ } , { } ) ;
341
+
342
+ return f . createObjectLiteralExpression ( Object . values ( aMap ) ) ;
343
+ } else {
344
+ return f . createObjectLiteralExpression ( [
345
+ f . createSpreadAssignment ( a ) ,
346
+ f . createSpreadAssignment ( b ) ,
347
+ ] ) ;
348
+ }
349
+ }
350
+
351
+ export function updateDecoratorArguments < T extends ts . ClassDeclaration | ts . PropertyDeclaration | ts . GetAccessorDeclaration > (
352
+ f : ts . NodeFactory ,
353
+ node : T ,
354
+ decoratorName : string ,
355
+ replaceFn : ( decoratorArguments : ts . NodeArray < ts . Expression > ) => ts . Expression [ ]
356
+ ) : T {
357
+ let updated = false ;
358
+
359
+ const decorators = node . decorators . map ( ( decorator ) => {
360
+ if ( getDecoratorName ( decorator ) !== decoratorName ) {
361
+ return decorator ;
362
+ }
363
+
364
+ const decoratorExpression = decorator . expression as ts . CallExpression ;
365
+ updated = true ;
366
+ return f . updateDecorator (
367
+ decorator ,
368
+ f . updateCallExpression (
369
+ decoratorExpression ,
370
+ decoratorExpression . expression ,
371
+ decoratorExpression . typeArguments ,
372
+ replaceFn ( decoratorExpression . arguments ) ,
373
+ ) ,
374
+ ) ;
375
+ } ) ;
376
+
377
+ if ( ! updated ) {
378
+ return node ;
379
+ }
380
+
381
+ if ( ts . isClassDeclaration ( node ) ) {
382
+ return f . updateClassDeclaration ( node , decorators , node . modifiers , node . name , node . typeParameters , node . heritageClauses , node . members ) as T ;
383
+ }
384
+
385
+ if ( ts . isPropertyDeclaration ( node ) ) {
386
+ return f . updatePropertyDeclaration ( node , decorators , node . modifiers , node . name , node . questionToken , node . type , node . initializer ) as T ;
387
+ }
388
+
389
+ if ( ts . isGetAccessorDeclaration ( node ) ) {
390
+ return f . updateGetAccessorDeclaration ( node , decorators , node . modifiers , node . name , node . parameters , node . type , node . body ) as T ;
391
+ }
392
+ }
0 commit comments