@@ -64,6 +64,7 @@ import {
64
64
GraphQLOneOfDirective ,
65
65
GraphQLSpecifiedByDirective ,
66
66
isSpecifiedDirective ,
67
+ specifiedDirectives ,
67
68
} from '../type/directives.js' ;
68
69
import {
69
70
introspectionTypes ,
@@ -116,7 +117,7 @@ export function extendSchema(
116
117
}
117
118
118
119
const schemaConfig = schema . toConfig ( ) ;
119
- const extendedConfig = extendSchemaImpl ( schemaConfig , documentAST , options ) ;
120
+ const extendedConfig = extendSchemaImpl ( documentAST , schemaConfig , options ) ;
120
121
return schemaConfig === extendedConfig
121
122
? schema
122
123
: new GraphQLSchema ( extendedConfig ) ;
@@ -126,10 +127,19 @@ export function extendSchema(
126
127
* @internal
127
128
*/
128
129
export function extendSchemaImpl (
129
- schemaConfig : GraphQLSchemaNormalizedConfig ,
130
130
documentAST : DocumentNode ,
131
+ originalSchemaConfig : GraphQLSchemaNormalizedConfig | undefined ,
131
132
options ?: Options ,
132
133
) : GraphQLSchemaNormalizedConfig {
134
+ const schemaConfig : GraphQLSchemaNormalizedConfig = originalSchemaConfig ?? {
135
+ description : undefined ,
136
+ types : [ ] ,
137
+ directives : [ ...specifiedDirectives ] ,
138
+ extensions : Object . create ( null ) ,
139
+ extensionASTNodes : [ ] ,
140
+ assumeValid : false ,
141
+ } ;
142
+
133
143
// Collect the type definitions and extensions found in the document.
134
144
const typeDefs : Array < TypeDefinitionNode > = [ ] ;
135
145
@@ -211,7 +221,12 @@ export function extendSchemaImpl(
211
221
// If this document contains no new types, extensions, or directives then
212
222
// return the same unmodified GraphQLSchema instance.
213
223
if ( ! isSchemaChanged ) {
214
- return schemaConfig ;
224
+ return originalSchemaConfig
225
+ ? originalSchemaConfig
226
+ : {
227
+ ...schemaConfig ,
228
+ directives : [ ...specifiedDirectives ] ,
229
+ } ;
215
230
}
216
231
217
232
const typeMap = new Map < string , GraphQLNamedType > (
@@ -230,19 +245,31 @@ export function extendSchemaImpl(
230
245
subscription :
231
246
schemaConfig . subscription && replaceNamedType ( schemaConfig . subscription ) ,
232
247
// Then, incorporate schema definition and all schema extensions.
233
- ...( schemaDef && getOperationTypes ( [ schemaDef ] ) ) ,
248
+ ...( schemaDef
249
+ ? getOperationTypes ( [ schemaDef ] )
250
+ : ! originalSchemaConfig && getDefaultOperationTypes ( ) ) ,
234
251
...getOperationTypes ( schemaExtensions ) ,
235
252
} ;
236
253
254
+ const newDirectives = directiveDefs . map ( buildDirective ) ;
255
+ const directives = originalSchemaConfig
256
+ ? [ ...schemaConfig . directives . map ( replaceDirective ) , ...newDirectives ]
257
+ : [
258
+ ...newDirectives ,
259
+ // If specified directives were not explicitly declared, add them.
260
+ ...specifiedDirectives . filter ( ( stdDirective ) =>
261
+ newDirectives . every (
262
+ ( directive ) => directive . name !== stdDirective . name ,
263
+ ) ,
264
+ ) ,
265
+ ] ;
266
+
237
267
// Then produce and return a Schema config with these types.
238
268
return {
239
269
description : schemaDef ?. description ?. value ?? schemaConfig . description ,
240
270
...operationTypes ,
241
271
types : Array . from ( typeMap . values ( ) ) ,
242
- directives : [
243
- ...schemaConfig . directives . map ( replaceDirective ) ,
244
- ...directiveDefs . map ( buildDirective ) ,
245
- ] ,
272
+ directives,
246
273
extensions : schemaConfig . extensions ,
247
274
astNode : schemaDef ?? schemaConfig . astNode ,
248
275
extensionASTNodes : schemaConfig . extensionASTNodes . concat ( schemaExtensions ) ,
@@ -431,6 +458,27 @@ export function extendSchemaImpl(
431
458
} ;
432
459
}
433
460
461
+ function getDefaultOperationTypes ( ) : {
462
+ query ?: Maybe < GraphQLObjectType > ;
463
+ mutation ?: Maybe < GraphQLObjectType > ;
464
+ subscription ?: Maybe < GraphQLObjectType > ;
465
+ } {
466
+ const opTypes = { } ;
467
+ for ( const typeName of [ 'Query' , 'Mutation' , 'Subscription' ] ) {
468
+ const operationType = typeMap . get ( typeName ) ;
469
+
470
+ if ( operationType ) {
471
+ // Note: While this could make early assertions to get the correctly
472
+ // typed values below, that would throw immediately while type system
473
+ // validation with validateSchema() will produce more actionable results.
474
+ // @ts -expect-error
475
+ opTypes [ typeName . toLowerCase ( ) ] = operationType ;
476
+ }
477
+ }
478
+
479
+ return opTypes ;
480
+ }
481
+
434
482
function getOperationTypes (
435
483
nodes : ReadonlyArray < SchemaDefinitionNode | SchemaExtensionNode > ,
436
484
) : {
0 commit comments