1
- import { ASTKindToNode , ListTypeNode , NamedTypeNode , parse , printSchema , TypeNode } from 'graphql' ;
1
+ import {
2
+ parse ,
3
+ printSchema ,
4
+ TypeNode ,
5
+ ASTKindToNode ,
6
+ ListTypeNode ,
7
+ NamedTypeNode ,
8
+ ObjectTypeDefinitionNode ,
9
+ } from 'graphql' ;
2
10
import { faker } from '@faker-js/faker' ;
3
11
import casual from 'casual' ;
4
12
import { oldVisit , PluginFunction , resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers' ;
@@ -26,6 +34,7 @@ type Options<T = TypeNode> = {
26
34
generateLibrary : 'casual' | 'faker' ;
27
35
fieldGeneration ?: TypeFieldMap ;
28
36
enumsAsTypes ?: boolean ;
37
+ useImplementingTypes : boolean ;
29
38
} ;
30
39
31
40
const convertName = ( value : string , fn : ( v : string ) => string , transformUnderscore : boolean ) : string => {
@@ -230,6 +239,17 @@ const handleValueGeneration = (
230
239
return baseGenerator ( ) ;
231
240
} ;
232
241
242
+ const getNamedImplementType = ( opts : Options < TypeItem [ 'types' ] > ) : string => {
243
+ if ( ! opts . currentType || ! ( 'name' in opts . currentType ) ) {
244
+ return '' ;
245
+ }
246
+
247
+ const name = opts . currentType . name . value ;
248
+ const casedName = createNameConverter ( opts . typeNamesConvention , opts . transformUnderscore ) ( name ) ;
249
+
250
+ return `${ toMockName ( name , casedName , opts . prefix ) } ()` ;
251
+ } ;
252
+
233
253
const getNamedType = ( opts : Options < NamedTypeNode > ) : string | number | boolean => {
234
254
if ( ! opts . currentType ) {
235
255
return '' ;
@@ -264,8 +284,14 @@ const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean =
264
284
return handleValueGeneration ( opts , customScalar , mockValueGenerator . integer ) ;
265
285
}
266
286
default : {
267
- const foundType = opts . types . find ( ( enumType : TypeItem ) => enumType . name === name ) ;
268
- if ( foundType ) {
287
+ const foundTypes = opts . types . filter ( ( foundType : TypeItem ) => {
288
+ if ( foundType . types && 'interfaces' in foundType . types )
289
+ return foundType . types . interfaces . every ( ( item ) => item . name . value === name ) ;
290
+ return foundType . name === name ;
291
+ } ) ;
292
+
293
+ if ( foundTypes . length ) {
294
+ const foundType = foundTypes [ 0 ] ;
269
295
switch ( foundType . type ) {
270
296
case 'enum' : {
271
297
// It's an enum
@@ -300,6 +326,22 @@ const getNamedType = (opts: Options<NamedTypeNode>): string | number | boolean =
300
326
foundType . name === 'Date' ? mockValueGenerator . date : mockValueGenerator . word ,
301
327
) ;
302
328
}
329
+ case 'implement' :
330
+ if (
331
+ opts . fieldGeneration &&
332
+ opts . fieldGeneration [ opts . typeName ] &&
333
+ opts . fieldGeneration [ opts . typeName ] [ opts . fieldName ]
334
+ )
335
+ break ;
336
+
337
+ return foundTypes
338
+ . map ( ( implementType : TypeItem ) =>
339
+ getNamedImplementType ( {
340
+ ...opts ,
341
+ currentType : implementType . types ,
342
+ } ) ,
343
+ )
344
+ . join ( ' || ' ) ;
303
345
default :
304
346
throw `foundType is unknown: ${ foundType . name } : ${ foundType . type } ` ;
305
347
}
@@ -470,13 +512,14 @@ export interface TypescriptMocksPluginConfig {
470
512
fieldGeneration ?: TypeFieldMap ;
471
513
locale ?: string ;
472
514
enumsAsTypes ?: boolean ;
515
+ useImplementingTypes ?: boolean ;
473
516
}
474
517
475
518
interface TypeItem {
476
519
name : string ;
477
- type : 'enum' | 'scalar' | 'union' ;
520
+ type : 'enum' | 'scalar' | 'union' | 'implement' ;
478
521
values ?: string [ ] ;
479
- types ?: readonly NamedTypeNode [ ] ;
522
+ types ?: readonly NamedTypeNode [ ] | ObjectTypeDefinitionNode ;
480
523
}
481
524
482
525
type VisitFn < TAnyNode , TVisitedNode = TAnyNode > = (
@@ -516,14 +559,15 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
516
559
const dynamicValues = ! ! config . dynamicValues ;
517
560
const generateLibrary = config . generateLibrary || 'casual' ;
518
561
const enumsAsTypes = config . enumsAsTypes ?? false ;
562
+ const useImplementingTypes = config . useImplementingTypes ?? false ;
519
563
520
564
if ( generateLibrary === 'faker' && config . locale ) {
521
565
faker . setLocale ( config . locale ) ;
522
566
}
523
567
524
568
// List of types that are enums
525
569
const types : TypeItem [ ] = [ ] ;
526
- const visitor : VisitorType = {
570
+ const typeVisitor : VisitorType = {
527
571
EnumTypeDefinition : ( node ) => {
528
572
const name = node . name . value ;
529
573
if ( ! types . find ( ( enumType : TypeItem ) => enumType . name === name ) ) {
@@ -544,6 +588,32 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
544
588
} ) ;
545
589
}
546
590
} ,
591
+ ObjectTypeDefinition : ( node ) => {
592
+ // This function triggered per each type
593
+ const typeName = node . name . value ;
594
+
595
+ if ( config . useImplementingTypes ) {
596
+ if ( ! types . find ( ( objectType ) => objectType . name === typeName ) ) {
597
+ node . interfaces . length &&
598
+ types . push ( {
599
+ name : typeName ,
600
+ type : 'implement' ,
601
+ types : node ,
602
+ } ) ;
603
+ }
604
+ }
605
+ } ,
606
+ ScalarTypeDefinition : ( node ) => {
607
+ const name = node . name . value ;
608
+ if ( ! types . find ( ( scalarType ) => scalarType . name === name ) ) {
609
+ types . push ( {
610
+ name,
611
+ type : 'scalar' ,
612
+ } ) ;
613
+ }
614
+ } ,
615
+ } ;
616
+ const visitor : VisitorType = {
547
617
FieldDefinition : ( node ) => {
548
618
const fieldName = node . name . value ;
549
619
@@ -568,6 +638,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
568
638
generateLibrary,
569
639
fieldGeneration : config . fieldGeneration ,
570
640
enumsAsTypes,
641
+ useImplementingTypes,
571
642
} ) ;
572
643
573
644
return ` ${ fieldName } : overrides && overrides.hasOwnProperty('${ fieldName } ') ? overrides.${ fieldName } ! : ${ value } ,` ;
@@ -601,6 +672,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
601
672
generateLibrary,
602
673
fieldGeneration : config . fieldGeneration ,
603
674
enumsAsTypes,
675
+ useImplementingTypes,
604
676
} ) ;
605
677
606
678
return ` ${ field . name . value } : overrides && overrides.hasOwnProperty('${ field . name . value } ') ? overrides.${ field . name . value } ! : ${ value } ,` ;
@@ -665,17 +737,10 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
665
737
} ,
666
738
} ;
667
739
} ,
668
- ScalarTypeDefinition : ( node ) => {
669
- const name = node . name . value ;
670
- if ( ! types . find ( ( enumType ) => enumType . name === name ) ) {
671
- types . push ( {
672
- name,
673
- type : 'scalar' ,
674
- } ) ;
675
- }
676
- } ,
677
740
} ;
678
741
742
+ // run on the types first
743
+ oldVisit ( astNode , { leave : typeVisitor } ) ;
679
744
const result = oldVisit ( astNode , { leave : visitor } ) ;
680
745
const definitions = result . definitions . filter ( ( definition : any ) => ! ! definition ) ;
681
746
const typesFile = config . typesFile ? config . typesFile . replace ( / \. [ \w ] + $ / , '' ) : null ;
0 commit comments