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 ,
@@ -171,56 +173,50 @@ export function extendSchema(
171
173
return schema ;
172
174
}
173
175
174
- const definitionBuilder = new ASTDefinitionBuilder (
176
+ const astBuilder = new ASTDefinitionBuilder (
175
177
typeDefinitionMap ,
176
178
options ,
177
- ( typeName , node ) => {
179
+ typeRef => {
180
+ const typeName = typeRef . name . value ;
178
181
const existingType = schema . getType ( typeName ) ;
179
182
if ( existingType ) {
180
183
return extendType ( existingType ) ;
181
184
}
182
185
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
+ ) ;
191
191
} ,
192
192
) ;
193
193
194
+ const extendTypeCache = Object . create ( null ) ;
195
+
194
196
// Get the root Query, Mutation, and Subscription object types.
195
197
// Note: While this could make early assertions to get the correctly
196
198
// typed values below, that would throw immediately while type system
197
199
// validation with validateSchema() will produce more actionable results.
198
200
const existingQueryType = schema . getQueryType ( ) ;
199
- const queryType = existingQueryType
200
- ? ( definitionBuilder . buildType ( existingQueryType . name ) : any )
201
- : null ;
201
+ const queryType = existingQueryType ? extendType ( existingQueryType ) : null ;
202
202
203
203
const existingMutationType = schema . getMutationType ( ) ;
204
204
const mutationType = existingMutationType
205
- ? ( definitionBuilder . buildType ( existingMutationType . name ) : any )
205
+ ? extendType ( existingMutationType )
206
206
: null ;
207
207
208
208
const existingSubscriptionType = schema . getSubscriptionType ( ) ;
209
209
const subscriptionType = existingSubscriptionType
210
- ? ( definitionBuilder . buildType ( existingSubscriptionType . name ) : any )
210
+ ? extendType ( existingSubscriptionType )
211
211
: null ;
212
212
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
+ ] ;
224
220
225
221
// Then produce and return a Schema with these types.
226
222
return new GraphQLSchema ( {
@@ -241,30 +237,29 @@ export function extendSchema(
241
237
const existingDirectives = schema . getDirectives ( ) ;
242
238
invariant ( existingDirectives , 'schema must have default directives' ) ;
243
239
244
- const newDirectives = directiveDefinitions . map ( directiveNode =>
245
- definitionBuilder . buildDirective ( directiveNode ) ,
240
+ return existingDirectives . concat (
241
+ directiveDefinitions . map ( node => astBuilder . buildDirective ( node ) ) ,
246
242
) ;
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 ) ;
253
243
}
254
244
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 ;
266
261
}
267
- return type ;
262
+ return ( extendedType : any ) ;
268
263
}
269
264
270
265
function extendObjectType ( type : GraphQLObjectType ) : GraphQLObjectType {
@@ -301,7 +296,7 @@ export function extendSchema(
301
296
return new GraphQLUnionType ( {
302
297
name : type . name ,
303
298
description : type . description ,
304
- types : type . getTypes ( ) . map ( getTypeFromDef ) ,
299
+ types : type . getTypes ( ) . map ( extendType ) ,
305
300
astNode : type . astNode ,
306
301
resolveType : type . resolveType ,
307
302
} ) ;
@@ -310,7 +305,7 @@ export function extendSchema(
310
305
function extendImplementedInterfaces (
311
306
type : GraphQLObjectType ,
312
307
) : Array < GraphQLInterfaceType > {
313
- const interfaces = type . getInterfaces ( ) . map ( getTypeFromDef ) ;
308
+ const interfaces = type . getInterfaces ( ) . map ( extendType ) ;
314
309
315
310
// If there are any extensions to the interfaces, apply those here.
316
311
const extensions = typeExtensionsMap [ type . name ] ;
@@ -320,7 +315,7 @@ export function extendSchema(
320
315
// Note: While this could make early assertions to get the correctly
321
316
// typed values, that would throw immediately while type system
322
317
// validation with validateSchema() will produce more actionable results.
323
- interfaces . push ( ( definitionBuilder . buildType ( namedType ) : any ) ) ;
318
+ interfaces . push ( ( astBuilder . buildType ( namedType ) : any ) ) ;
324
319
} ) ;
325
320
} ) ;
326
321
}
@@ -356,7 +351,7 @@ export function extendSchema(
356
351
[ field ] ,
357
352
) ;
358
353
}
359
- newFieldMap [ fieldName ] = definitionBuilder . buildField ( field ) ;
354
+ newFieldMap [ fieldName ] = astBuilder . buildField ( field ) ;
360
355
} ) ;
361
356
} ) ;
362
357
}
@@ -371,6 +366,6 @@ export function extendSchema(
371
366
if ( isNonNullType ( typeDef ) ) {
372
367
return ( GraphQLNonNull ( extendFieldType ( typeDef . ofType ) ) : any ) ;
373
368
}
374
- return getTypeFromDef (typeDef);
369
+ return extendType ( typeDef ) ;
375
370
}
376
371
}
0 commit comments