9
9
10
10
import invariant from '../jsutils/invariant' ;
11
11
import keyMap from '../jsutils/keyMap' ;
12
+ import objectValues from '../jsutils/objectValues' ;
12
13
import { ASTDefinitionBuilder } from './buildASTSchema' ;
13
14
import { GraphQLError } from '../error/GraphQLError' ;
14
15
import { isSchema , GraphQLSchema } from '../type/schema' ;
16
+ import { isIntrospectionType } from '../type/introspection' ;
15
17
16
18
import {
17
19
isObjectType ,
@@ -194,56 +196,50 @@ export function extendSchema(
194
196
return schema ;
195
197
}
196
198
197
- const definitionBuilder = new ASTDefinitionBuilder (
199
+ const astBuilder = new ASTDefinitionBuilder (
198
200
typeDefinitionMap ,
199
201
options ,
200
- ( typeName , node ) => {
202
+ typeRef => {
203
+ const typeName = typeRef . name . value ;
201
204
const existingType = schema . getType ( typeName ) ;
202
205
if ( existingType ) {
203
206
return extendType ( existingType ) ;
204
207
}
205
208
206
- if ( node ) {
207
- throw new GraphQLError (
208
- `Unknown type: "${ typeName } ". Ensure that this type exists ` +
209
- 'either in the original schema, or is added in a type definition.' ,
210
- [ node ] ,
211
- ) ;
212
- }
213
- throw GraphQLError ( 'Missing type from schema' ) ;
209
+ throw new GraphQLError (
210
+ `Unknown type: "${ typeName } ". Ensure that this type exists ` +
211
+ 'either in the original schema, or is added in a type definition.' ,
212
+ [ typeRef ] ,
213
+ ) ;
214
214
} ,
215
215
) ;
216
216
217
+ const extendTypeCache = Object . create ( null ) ;
218
+
217
219
// Get the root Query, Mutation, and Subscription object types.
218
220
// Note: While this could make early assertions to get the correctly
219
221
// typed values below, that would throw immediately while type system
220
222
// validation with validateSchema() will produce more actionable results.
221
223
const existingQueryType = schema . getQueryType ( ) ;
222
- const queryType = existingQueryType
223
- ? ( definitionBuilder . buildType ( existingQueryType . name ) : any )
224
- : null ;
224
+ const queryType = existingQueryType ? extendType ( existingQueryType ) : null ;
225
225
226
226
const existingMutationType = schema . getMutationType ( ) ;
227
227
const mutationType = existingMutationType
228
- ? ( definitionBuilder . buildType ( existingMutationType . name ) : any )
228
+ ? extendType ( existingMutationType )
229
229
: null ;
230
230
231
231
const existingSubscriptionType = schema . getSubscriptionType ( ) ;
232
232
const subscriptionType = existingSubscriptionType
233
- ? ( definitionBuilder . buildType ( existingSubscriptionType . name ) : any )
233
+ ? extendType ( existingSubscriptionType )
234
234
: null ;
235
235
236
- // Iterate through all types, getting the type definition for each, ensuring
237
- // that any type not directly referenced by a field will get created.
238
- const typeMap = schema . getTypeMap ( ) ;
239
- const types = Object . keys ( typeMap ) . map ( typeName =>
240
- definitionBuilder . buildType ( typeName ) ,
241
- ) ;
242
-
243
- // Do the same with new types, appending to the list of defined types.
244
- Object . keys ( typeDefinitionMap ) . forEach ( typeName => {
245
- types . push ( definitionBuilder . buildType ( typeName ) ) ;
246
- } ) ;
236
+ const types = [
237
+ // Iterate through all types, getting the type definition for each, ensuring
238
+ // that any type not directly referenced by a field will get created.
239
+ ...objectValues ( schema . getTypeMap ( ) ) . map ( type => extendType ( type ) ) ,
240
+ // Do the same with new types.
241
+ ...objectValues ( typeDefinitionMap ) . map ( type => astBuilder . buildType ( type ) ) ,
242
+ ] ;
247
243
248
244
// Then produce and return a Schema with these types.
249
245
return new GraphQLSchema ( {
@@ -275,30 +271,29 @@ export function extendSchema(
275
271
const existingDirectives = schema . getDirectives ( ) ;
276
272
invariant ( existingDirectives , 'schema must have default directives' ) ;
277
273
278
- const newDirectives = directiveDefinitions . map ( directiveNode =>
279
- definitionBuilder . buildDirective ( directiveNode ) ,
274
+ return existingDirectives . concat (
275
+ directiveDefinitions . map ( node => astBuilder . buildDirective ( node ) ) ,
280
276
) ;
281
- return existingDirectives . concat ( newDirectives ) ;
282
- }
283
-
284
- function getTypeFromDef < T : GraphQLNamedType > (typeDef: T): T {
285
- const type = definitionBuilder . buildType ( typeDef . name ) ;
286
- return ( type : any ) ;
287
277
}
288
278
289
- // Given a type's introspection result, construct the correct
290
- // GraphQLType instance.
291
- function extendType(type: GraphQLNamedType): GraphQLNamedType {
292
- if ( isObjectType ( type ) ) {
293
- return extendObjectType ( type ) ;
294
- }
295
- if (isInterfaceType(type)) {
296
- return extendInterfaceType ( type ) ;
297
- }
298
- if (isUnionType(type)) {
299
- return extendUnionType ( type ) ;
279
+ function extendType < T : GraphQLNamedType > (type: T): T {
280
+ let extendedType = extendTypeCache [ type . name ] ;
281
+
282
+ if ( ! extendedType ) {
283
+ if ( isIntrospectionType ( type ) ) {
284
+ extendedType = type ;
285
+ } else if ( isObjectType ( type ) ) {
286
+ extendedType = extendObjectType ( type ) ;
287
+ } else if ( isInterfaceType ( type ) ) {
288
+ extendedType = extendInterfaceType ( type ) ;
289
+ } else if ( isUnionType ( type ) ) {
290
+ extendedType = extendUnionType ( type ) ;
291
+ } else {
292
+ extendedType = type ;
293
+ }
294
+ extendTypeCache [ type . name ] = extendedType ;
300
295
}
301
- return type ;
296
+ return (extendedType: any) ;
302
297
}
303
298
304
299
function extendObjectType ( type : GraphQLObjectType ) : GraphQLObjectType {
@@ -342,7 +337,7 @@ export function extendSchema(
342
337
return new GraphQLUnionType ( {
343
338
name : type . name ,
344
339
description : type . description ,
345
- types : type . getTypes ( ) . map ( getTypeFromDef ) ,
340
+ types : type . getTypes ( ) . map ( extendType ) ,
346
341
astNode : type . astNode ,
347
342
resolveType : type . resolveType ,
348
343
} ) ;
@@ -351,7 +346,7 @@ export function extendSchema(
351
346
function extendImplementedInterfaces(
352
347
type: GraphQLObjectType,
353
348
): Array< GraphQLInterfaceType > {
354
- const interfaces = type . getInterfaces ( ) . map ( getTypeFromDef ) ;
349
+ const interfaces = type . getInterfaces ( ) . map ( extendType ) ;
355
350
356
351
// If there are any extensions to the interfaces, apply those here.
357
352
const extensions = typeExtensionsMap [ type . name ] ;
@@ -361,7 +356,7 @@ export function extendSchema(
361
356
// Note: While this could make early assertions to get the correctly
362
357
// typed values, that would throw immediately while type system
363
358
// validation with validateSchema() will produce more actionable results.
364
- interfaces . push ( ( definitionBuilder . buildType ( namedType ) : any ) ) ;
359
+ interfaces . push ( ( astBuilder . buildType ( namedType ) : any ) ) ;
365
360
} ) ;
366
361
} ) ;
367
362
}
@@ -397,7 +392,7 @@ export function extendSchema(
397
392
[ field ] ,
398
393
) ;
399
394
}
400
- newFieldMap [ fieldName ] = definitionBuilder . buildField ( field ) ;
395
+ newFieldMap [ fieldName ] = astBuilder . buildField ( field ) ;
401
396
} ) ;
402
397
} ) ;
403
398
}
@@ -412,6 +407,6 @@ export function extendSchema(
412
407
if (isNonNullType(typeDef)) {
413
408
return ( GraphQLNonNull ( extendFieldType ( typeDef . ofType ) ) : any ) ;
414
409
}
415
- return getTypeFromDef (typeDef);
410
+ return extendType (typeDef);
416
411
}
417
412
}
0 commit comments