@@ -104,13 +104,29 @@ export default class SchemaCodeGenerator {
104
104
. filter ( Boolean ) as Array < tsCodegen . Class > ;
105
105
}
106
106
107
+ generateDerivedLoaders ( ) {
108
+ const fields = this . schema . ast . definitions
109
+ . filter ( ( def ) => this . _isEntityTypeDefinition ( def ) )
110
+ . flatMap ( ( def : any ) => def . fields )
111
+ . filter ( ( def : any ) => this . _isDerivedField ( def ) )
112
+ . map ( ( def : any ) => this . _getTypeNameForField ( def ) ) ;
113
+
114
+ return [ ...new Set ( fields ) ] . map ( ( typeName : any ) => this . _generateDerivedLoader ( typeName ) ) ;
115
+ }
116
+
107
117
_isEntityTypeDefinition ( def : DefinitionNode ) : def is ObjectTypeDefinitionNode {
108
118
return (
109
119
def . kind === 'ObjectTypeDefinition' &&
110
120
def . directives ?. find ( directive => directive . name . value === 'entity' ) !== undefined
111
121
) ;
112
122
}
113
123
124
+
125
+ _isDerivedField ( field : any ) : boolean {
126
+ return field . directives ?. find (
127
+ ( directive : any ) => directive . name . value === 'derivedFrom'
128
+ ) !== undefined
129
+ }
114
130
_isInterfaceDefinition ( def : DefinitionNode ) : def is InterfaceTypeDefinitionNode {
115
131
return def . kind === 'InterfaceTypeDefinition' ;
116
132
}
@@ -138,6 +154,39 @@ export default class SchemaCodeGenerator {
138
154
return klass ;
139
155
}
140
156
157
+
158
+ _generateDerivedLoader ( typeName : string ) : any {
159
+ // <field>Loader
160
+ const klass = tsCodegen . klass ( `${ typeName } Loader` , { export : true , extends : 'Entity' } ) ;
161
+
162
+ klass . addMember ( tsCodegen . klassMember ( "_entity" , "string" ) )
163
+ klass . addMember ( tsCodegen . klassMember ( "_field" , "string" ) )
164
+ klass . addMember ( tsCodegen . klassMember ( "_id" , "string" ) )
165
+ // Generate and add a constructor
166
+ klass . addMethod ( tsCodegen . method ( 'constructor' , [ tsCodegen . param ( 'entity' , 'string' ) , tsCodegen . param ( 'id' , 'string' ) , tsCodegen . param ( 'field' , 'string' ) ] , undefined , `
167
+ super();
168
+ this._entity = entity;
169
+ this._id = id;
170
+ this._field = field;
171
+ ` ) ) ;
172
+
173
+ // Generate load() method for the Loader
174
+ klass . addMethod ( tsCodegen . method ( 'load' , [ ] , `${ typeName } []` , `
175
+ let value = store.loadRelated(this._entity, this._id, this._field);
176
+ return changetype<${ typeName } []>(value);
177
+ ` ) )
178
+
179
+ return klass ;
180
+ }
181
+ _getTypeNameForField ( gqlType : any ) : any {
182
+ if ( gqlType . kind === 'NonNullType' ) {
183
+ return this . _getTypeNameForField ( gqlType . type )
184
+ } else if ( gqlType . kind === 'ListType' ) {
185
+ return this . _getTypeNameForField ( gqlType . type )
186
+ } else {
187
+ return gqlType . name . value
188
+ }
189
+ }
141
190
_generateConstructor ( _entityName : string , fields : readonly FieldDefinitionNode [ ] | undefined ) {
142
191
const idField = IdField . fromFields ( fields ) ;
143
192
return tsCodegen . method (
@@ -206,6 +255,7 @@ export default class SchemaCodeGenerator {
206
255
) ;
207
256
}
208
257
258
+
209
259
_generateEntityFieldGetter ( _entityDef : ObjectTypeDefinitionNode , fieldDef : FieldDefinitionNode ) {
210
260
const name = fieldDef . name . value ;
211
261
const gqlType = fieldDef . type ;
@@ -240,7 +290,60 @@ export default class SchemaCodeGenerator {
240
290
` ,
241
291
) ;
242
292
}
293
+ _generateDerivedFieldGetter ( entityDef : any , fieldDef : FieldDefinitionNode ) {
294
+ let entityName = entityDef . name . value ;
295
+ let name = fieldDef . name . value ;
296
+ let gqlType = fieldDef . type ;
297
+ let returnType = this . _returnTypeForDervied ( gqlType )
298
+ return tsCodegen . method (
299
+ `get ${ name } ` ,
300
+ [ ] ,
301
+ returnType ,
302
+ `
303
+ return new ${ returnType } ('${ entityName } ', this.get('id')!.toString(), '${ name } ')
304
+ ` ,
305
+ )
306
+ }
307
+
243
308
309
+ _returnTypeForDervied ( gqlType : any ) : any {
310
+ if ( gqlType . kind === 'NonNullType' ) {
311
+ return this . _returnTypeForDervied ( gqlType . type )
312
+ } else if ( gqlType . kind === 'ListType' ) {
313
+ return this . _returnTypeForDervied ( gqlType . type )
314
+ } else {
315
+
316
+ const type = tsCodegen . namedType (
317
+ gqlType . name . value + 'Loader'
318
+ ) ;
319
+ return type ;
320
+ }
321
+ }
322
+
323
+ _generatedEntityDerivedFieldGetter ( _entityDef : any , fieldDef : FieldDefinitionNode ) {
324
+ const name = fieldDef . name . value ;
325
+ const gqlType = fieldDef . type ;
326
+ const fieldValueType = this . _valueTypeFromGraphQl ( gqlType ) ;
327
+ const returnType = this . _typeFromGraphQl ( gqlType ) ;
328
+ const isNullable = returnType instanceof tsCodegen . NullableType ;
329
+
330
+ const getNonNullable = `return ${ typesCodegen . valueToAsc ( 'value!' , fieldValueType ) } ` ;
331
+ const getNullable = `if (!value || value.kind == ValueKind.NULL) {
332
+ return null
333
+ } else {
334
+ return ${ typesCodegen . valueToAsc ( 'value' , fieldValueType ) }
335
+ }` ;
336
+
337
+ return tsCodegen . method (
338
+ `get ${ name } ` ,
339
+ [ ] ,
340
+ returnType ,
341
+ `
342
+ let value = this.get('${ name } ')
343
+ ${ isNullable ? getNullable : getNonNullable }
344
+ ` ,
345
+ ) ;
346
+ }
244
347
_generateEntityFieldSetter ( _entityDef : ObjectTypeDefinitionNode , fieldDef : FieldDefinitionNode ) {
245
348
const name = fieldDef . name . value ;
246
349
const isDerivedField = ! ! fieldDef . directives ?. find (
@@ -349,3 +452,4 @@ Suggestion: add an '!' to the member type of the List, change from '[${baseType}
349
452
return nullable && ! type . isPrimitive ( ) ? tsCodegen . nullableType ( type ) : type ;
350
453
}
351
454
}
455
+
0 commit comments