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 ,
@@ -171,56 +173,50 @@ export function extendSchema(
171173 return schema ;
172174 }
173175
174- const definitionBuilder = new ASTDefinitionBuilder (
176+ const astBuilder = new ASTDefinitionBuilder (
175177 typeDefinitionMap ,
176178 options ,
177- ( typeName , node ) => {
179+ typeRef => {
180+ const typeName = typeRef . name . value ;
178181 const existingType = schema . getType ( typeName ) ;
179182 if ( existingType ) {
180183 return extendType ( existingType ) ;
181184 }
182185
183- if ( node ) {
184- throw new GraphQLError (
185- `Unknown type: "${ typeName } ". Ensure that this type exists ` +
186- 'either in the original schema, or is added in a type definition.' ,
187- [ node ] ,
188- ) ;
189- }
190- throw GraphQLError ( 'Missing type from schema' ) ;
186+ throw new GraphQLError (
187+ `Unknown type: "${ typeName } ". Ensure that this type exists ` +
188+ 'either in the original schema, or is added in a type definition.' ,
189+ [ typeRef ] ,
190+ ) ;
191191 } ,
192192 ) ;
193193
194+ const extendTypeCache = Object . create ( null ) ;
195+
194196 // Get the root Query, Mutation, and Subscription object types.
195197 // Note: While this could make early assertions to get the correctly
196198 // typed values below, that would throw immediately while type system
197199 // validation with validateSchema() will produce more actionable results.
198200 const existingQueryType = schema . getQueryType ( ) ;
199- const queryType = existingQueryType
200- ? ( definitionBuilder . buildType ( existingQueryType . name ) : any )
201- : null ;
201+ const queryType = existingQueryType ? extendType ( existingQueryType ) : null ;
202202
203203 const existingMutationType = schema . getMutationType ( ) ;
204204 const mutationType = existingMutationType
205- ? ( definitionBuilder . buildType ( existingMutationType . name ) : any )
205+ ? extendType ( existingMutationType )
206206 : null ;
207207
208208 const existingSubscriptionType = schema . getSubscriptionType ( ) ;
209209 const subscriptionType = existingSubscriptionType
210- ? ( definitionBuilder . buildType ( existingSubscriptionType . name ) : any )
210+ ? extendType ( existingSubscriptionType )
211211 : null ;
212212
213- // Iterate through all types, getting the type definition for each, ensuring
214- // that any type not directly referenced by a field will get created.
215- const typeMap = schema . getTypeMap ( ) ;
216- const types = Object . keys ( typeMap ) . map ( typeName =>
217- definitionBuilder . buildType ( typeName ) ,
218- ) ;
219-
220- // Do the same with new types, appending to the list of defined types.
221- Object . keys ( typeDefinitionMap ) . forEach ( typeName => {
222- types . push ( definitionBuilder . buildType ( typeName ) ) ;
223- } ) ;
213+ const types = [
214+ // Iterate through all types, getting the type definition for each, ensuring
215+ // that any type not directly referenced by a field will get created.
216+ ...objectValues ( schema . getTypeMap ( ) ) . map ( type => extendType ( type ) ) ,
217+ // Do the same with new types.
218+ ...objectValues ( typeDefinitionMap ) . map ( type => astBuilder . buildType ( type ) ) ,
219+ ] ;
224220
225221 // Then produce and return a Schema with these types.
226222 return new GraphQLSchema ( {
@@ -241,30 +237,29 @@ export function extendSchema(
241237 const existingDirectives = schema . getDirectives ( ) ;
242238 invariant ( existingDirectives , 'schema must have default directives' ) ;
243239
244- const newDirectives = directiveDefinitions . map ( directiveNode =>
245- definitionBuilder . buildDirective ( directiveNode ) ,
240+ return existingDirectives . concat (
241+ directiveDefinitions . map ( node => astBuilder . buildDirective ( node ) ) ,
246242 ) ;
247- return existingDirectives . concat ( newDirectives ) ;
248- }
249-
250- function getTypeFromDef < T : GraphQLNamedType > (typeDef: T): T {
251- const type = definitionBuilder . buildType ( typeDef . name ) ;
252- return ( type : any ) ;
253243 }
254244
255- // Given a type's introspection result, construct the correct
256- // GraphQLType instance.
257- function extendType(type: GraphQLNamedType): GraphQLNamedType {
258- if ( isObjectType ( type ) ) {
259- return extendObjectType ( type ) ;
260- }
261- if (isInterfaceType(type)) {
262- return extendInterfaceType ( type ) ;
263- }
264- if (isUnionType(type)) {
265- return extendUnionType ( type ) ;
245+ function extendType < T : GraphQLNamedType > ( type : T ) : T {
246+ let extendedType = extendTypeCache [ type . name ] ;
247+
248+ if ( ! extendedType ) {
249+ if ( isIntrospectionType ( type ) ) {
250+ extendedType = type ;
251+ } else if ( isObjectType ( type ) ) {
252+ extendedType = extendObjectType ( type ) ;
253+ } else if ( isInterfaceType ( type ) ) {
254+ extendedType = extendInterfaceType ( type ) ;
255+ } else if ( isUnionType ( type ) ) {
256+ extendedType = extendUnionType ( type ) ;
257+ } else {
258+ extendedType = type ;
259+ }
260+ extendTypeCache [ type . name ] = extendedType ;
266261 }
267- return type ;
262+ return ( extendedType : any ) ;
268263 }
269264
270265 function extendObjectType ( type : GraphQLObjectType ) : GraphQLObjectType {
@@ -301,7 +296,7 @@ export function extendSchema(
301296 return new GraphQLUnionType ( {
302297 name : type . name ,
303298 description : type . description ,
304- types : type . getTypes ( ) . map ( getTypeFromDef ) ,
299+ types : type . getTypes ( ) . map ( extendType ) ,
305300 astNode : type . astNode ,
306301 resolveType : type . resolveType ,
307302 } ) ;
@@ -310,7 +305,7 @@ export function extendSchema(
310305 function extendImplementedInterfaces (
311306 type : GraphQLObjectType ,
312307 ) : Array < GraphQLInterfaceType > {
313- const interfaces = type . getInterfaces ( ) . map ( getTypeFromDef ) ;
308+ const interfaces = type . getInterfaces ( ) . map ( extendType ) ;
314309
315310 // If there are any extensions to the interfaces, apply those here.
316311 const extensions = typeExtensionsMap [ type . name ] ;
@@ -320,7 +315,7 @@ export function extendSchema(
320315 // Note: While this could make early assertions to get the correctly
321316 // typed values, that would throw immediately while type system
322317 // validation with validateSchema() will produce more actionable results.
323- interfaces . push ( ( definitionBuilder . buildType ( namedType ) : any ) ) ;
318+ interfaces . push ( ( astBuilder . buildType ( namedType ) : any ) ) ;
324319 } ) ;
325320 } ) ;
326321 }
@@ -356,7 +351,7 @@ export function extendSchema(
356351 [ field ] ,
357352 ) ;
358353 }
359- newFieldMap [ fieldName ] = definitionBuilder . buildField ( field ) ;
354+ newFieldMap [ fieldName ] = astBuilder . buildField ( field ) ;
360355 } ) ;
361356 } ) ;
362357 }
@@ -371,6 +366,6 @@ export function extendSchema(
371366 if ( isNonNullType ( typeDef ) ) {
372367 return ( GraphQLNonNull ( extendFieldType ( typeDef . ofType ) ) : any ) ;
373368 }
374- return getTypeFromDef (typeDef);
369+ return extendType ( typeDef ) ;
375370 }
376371}
0 commit comments