99
1010import invariant from '../jsutils/invariant' ;
1111import keyMap from '../jsutils/keyMap' ;
12+ import objectValues from '../jsutils/objectValues' ;
1213import { ASTDefinitionBuilder } from './buildASTSchema' ;
1314import { GraphQLError } from '../error/GraphQLError' ;
1415import { isSchema , GraphQLSchema } from '../type/schema' ;
16+ import { isIntrospectionType } from '../type/introspection' ;
1517
1618import {
1719 isObjectType ,
@@ -194,56 +196,50 @@ export function extendSchema(
194196 return schema ;
195197 }
196198
197- const definitionBuilder = new ASTDefinitionBuilder (
199+ const astBuilder = new ASTDefinitionBuilder (
198200 typeDefinitionMap ,
199201 options ,
200- ( typeName , node ) => {
202+ typeRef => {
203+ const typeName = typeRef . name . value ;
201204 const existingType = schema . getType ( typeName ) ;
202205 if ( existingType ) {
203206 return extendType ( existingType ) ;
204207 }
205208
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+ ) ;
214214 } ,
215215 ) ;
216216
217+ const extendTypeCache = Object . create ( null ) ;
218+
217219 // Get the root Query, Mutation, and Subscription object types.
218220 // Note: While this could make early assertions to get the correctly
219221 // typed values below, that would throw immediately while type system
220222 // validation with validateSchema() will produce more actionable results.
221223 const existingQueryType = schema . getQueryType ( ) ;
222- const queryType = existingQueryType
223- ? ( definitionBuilder . buildType ( existingQueryType . name ) : any )
224- : null ;
224+ const queryType = existingQueryType ? extendType ( existingQueryType ) : null ;
225225
226226 const existingMutationType = schema . getMutationType ( ) ;
227227 const mutationType = existingMutationType
228- ? ( definitionBuilder . buildType ( existingMutationType . name ) : any )
228+ ? extendType ( existingMutationType )
229229 : null ;
230230
231231 const existingSubscriptionType = schema . getSubscriptionType ( ) ;
232232 const subscriptionType = existingSubscriptionType
233- ? ( definitionBuilder . buildType ( existingSubscriptionType . name ) : any )
233+ ? extendType ( existingSubscriptionType )
234234 : null ;
235235
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+ ] ;
247243
248244 // Then produce and return a Schema with these types.
249245 return new GraphQLSchema ( {
@@ -275,30 +271,29 @@ export function extendSchema(
275271 const existingDirectives = schema . getDirectives ( ) ;
276272 invariant ( existingDirectives , 'schema must have default directives' ) ;
277273
278- const newDirectives = directiveDefinitions . map ( directiveNode =>
279- definitionBuilder . buildDirective ( directiveNode ) ,
274+ return existingDirectives . concat (
275+ directiveDefinitions . map ( node => astBuilder . buildDirective ( node ) ) ,
280276 ) ;
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 ) ;
287277 }
288278
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 ;
300295 }
301- return type ;
296+ return (extendedType: any) ;
302297 }
303298
304299 function extendObjectType ( type : GraphQLObjectType ) : GraphQLObjectType {
@@ -342,7 +337,7 @@ export function extendSchema(
342337 return new GraphQLUnionType ( {
343338 name : type . name ,
344339 description : type . description ,
345- types : type . getTypes ( ) . map ( getTypeFromDef ) ,
340+ types : type . getTypes ( ) . map ( extendType ) ,
346341 astNode : type . astNode ,
347342 resolveType : type . resolveType ,
348343 } ) ;
@@ -351,7 +346,7 @@ export function extendSchema(
351346 function extendImplementedInterfaces(
352347 type: GraphQLObjectType,
353348 ): Array< GraphQLInterfaceType > {
354- const interfaces = type . getInterfaces ( ) . map ( getTypeFromDef ) ;
349+ const interfaces = type . getInterfaces ( ) . map ( extendType ) ;
355350
356351 // If there are any extensions to the interfaces, apply those here.
357352 const extensions = typeExtensionsMap [ type . name ] ;
@@ -361,7 +356,7 @@ export function extendSchema(
361356 // Note: While this could make early assertions to get the correctly
362357 // typed values, that would throw immediately while type system
363358 // validation with validateSchema() will produce more actionable results.
364- interfaces . push ( ( definitionBuilder . buildType ( namedType ) : any ) ) ;
359+ interfaces . push ( ( astBuilder . buildType ( namedType ) : any ) ) ;
365360 } ) ;
366361 } ) ;
367362 }
@@ -397,7 +392,7 @@ export function extendSchema(
397392 [ field ] ,
398393 ) ;
399394 }
400- newFieldMap [ fieldName ] = definitionBuilder . buildField ( field ) ;
395+ newFieldMap [ fieldName ] = astBuilder . buildField ( field ) ;
401396 } ) ;
402397 } ) ;
403398 }
@@ -412,6 +407,6 @@ export function extendSchema(
412407 if (isNonNullType(typeDef)) {
413408 return ( GraphQLNonNull ( extendFieldType ( typeDef . ofType ) ) : any ) ;
414409 }
415- return getTypeFromDef (typeDef);
410+ return extendType (typeDef);
416411 }
417412}
0 commit comments