diff --git a/.changeset/cyan-ears-knock.md b/.changeset/cyan-ears-knock.md new file mode 100644 index 00000000000..7b11191d304 --- /dev/null +++ b/.changeset/cyan-ears-knock.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/merge': patch +--- + +Don't merge/override arguments for repeatable directives diff --git a/packages/merge/src/typedefs-mergers/directives.ts b/packages/merge/src/typedefs-mergers/directives.ts index 1cffa9aab41..cbb68b24ba1 100644 --- a/packages/merge/src/typedefs-mergers/directives.ts +++ b/packages/merge/src/typedefs-mergers/directives.ts @@ -6,6 +6,13 @@ function directiveAlreadyExists(directivesArr: ReadonlyArray, oth return !!directivesArr.find(directive => directive.name.value === otherDirective.name.value); } +function isRepeatableDirective( + directive: DirectiveNode, + directives?: Record +): boolean { + return !!directives?.[directive.name.value]?.repeatable; +} + function nameAlreadyExists(name: NameNode, namesArr: ReadonlyArray): boolean { return namesArr.some(({ value }) => value === name.value); } @@ -39,12 +46,15 @@ function mergeArguments(a1: readonly ArgumentNode[], a2: readonly ArgumentNode[] return result; } -function deduplicateDirectives(directives: ReadonlyArray): DirectiveNode[] { +function deduplicateDirectives( + directives: ReadonlyArray, + definitions?: Record +): DirectiveNode[] { return directives .map((directive, i, all) => { const firstAt = all.findIndex(d => d.name.value === directive.name.value); - if (firstAt !== i) { + if (firstAt !== i && !isRepeatableDirective(directive, definitions)) { const dup = all[firstAt]; (directive as any).arguments = mergeArguments(directive.arguments as any, dup.arguments as any); @@ -59,15 +69,16 @@ function deduplicateDirectives(directives: ReadonlyArray): Direct export function mergeDirectives( d1: ReadonlyArray = [], d2: ReadonlyArray = [], - config?: Config + config?: Config, + directives?: Record ): DirectiveNode[] { const reverseOrder: boolean | undefined = config && config.reverseDirectives; const asNext = reverseOrder ? d1 : d2; const asFirst = reverseOrder ? d2 : d1; - const result = deduplicateDirectives([...asNext]); + const result = deduplicateDirectives([...asNext], directives); for (const directive of asFirst) { - if (directiveAlreadyExists(result, directive)) { + if (directiveAlreadyExists(result, directive) && !isRepeatableDirective(directive, directives)) { const existingDirectiveIndex = result.findIndex(d => d.name.value === directive.name.value); const existingDirective = result[existingDirectiveIndex]; (result[existingDirectiveIndex] as any).arguments = mergeArguments( diff --git a/packages/merge/src/typedefs-mergers/enum-values.ts b/packages/merge/src/typedefs-mergers/enum-values.ts index 377d6bdbdc2..366f92c5ead 100644 --- a/packages/merge/src/typedefs-mergers/enum-values.ts +++ b/packages/merge/src/typedefs-mergers/enum-values.ts @@ -1,4 +1,4 @@ -import { EnumValueDefinitionNode } from 'graphql'; +import { DirectiveDefinitionNode, EnumValueDefinitionNode } from 'graphql'; import { mergeDirectives } from './directives.js'; import { Config } from './merge-typedefs.js'; import { compareNodes } from '@graphql-tools/utils'; @@ -6,7 +6,8 @@ import { compareNodes } from '@graphql-tools/utils'; export function mergeEnumValues( first: ReadonlyArray | undefined, second: ReadonlyArray | undefined, - config?: Config + config?: Config, + directives?: Record ): EnumValueDefinitionNode[] { if (config?.consistentEnumMerge) { const reversed: Array = []; @@ -28,7 +29,7 @@ export function mergeEnumValues( if (enumValueMap.has(enumValue)) { const firstValue: any = enumValueMap.get(enumValue); firstValue.description = secondValue.description || firstValue.description; - firstValue.directives = mergeDirectives(secondValue.directives, firstValue.directives); + firstValue.directives = mergeDirectives(secondValue.directives, firstValue.directives, directives); } else { enumValueMap.set(enumValue, secondValue); } diff --git a/packages/merge/src/typedefs-mergers/enum.ts b/packages/merge/src/typedefs-mergers/enum.ts index a80082fcc26..49a0c855244 100644 --- a/packages/merge/src/typedefs-mergers/enum.ts +++ b/packages/merge/src/typedefs-mergers/enum.ts @@ -1,4 +1,4 @@ -import { EnumTypeDefinitionNode, EnumTypeExtensionNode, Kind } from 'graphql'; +import { DirectiveDefinitionNode, EnumTypeDefinitionNode, EnumTypeExtensionNode, Kind } from 'graphql'; import { mergeDirectives } from './directives.js'; import { mergeEnumValues } from './enum-values.js'; import { Config } from './merge-typedefs.js'; @@ -6,7 +6,8 @@ import { Config } from './merge-typedefs.js'; export function mergeEnum( e1: EnumTypeDefinitionNode | EnumTypeExtensionNode, e2: EnumTypeDefinitionNode | EnumTypeExtensionNode, - config?: Config + config?: Config, + directives?: Record ): EnumTypeDefinitionNode | EnumTypeExtensionNode { if (e2) { return { @@ -17,7 +18,7 @@ export function mergeEnum( ? 'EnumTypeDefinition' : 'EnumTypeExtension', loc: e1.loc, - directives: mergeDirectives(e1.directives, e2.directives, config), + directives: mergeDirectives(e1.directives, e2.directives, config, directives), values: mergeEnumValues(e1.values, e2.values, config), } as any; } diff --git a/packages/merge/src/typedefs-mergers/fields.ts b/packages/merge/src/typedefs-mergers/fields.ts index 8075efa43b8..a969cbc25ce 100644 --- a/packages/merge/src/typedefs-mergers/fields.ts +++ b/packages/merge/src/typedefs-mergers/fields.ts @@ -1,5 +1,5 @@ import { Config } from './merge-typedefs.js'; -import { FieldDefinitionNode, InputValueDefinitionNode, TypeNode, NameNode } from 'graphql'; +import { FieldDefinitionNode, InputValueDefinitionNode, TypeNode, NameNode, DirectiveDefinitionNode } from 'graphql'; import { extractType, isWrappingTypeNode, isListTypeNode, isNonNullTypeNode, printTypeNode } from './utils.js'; import { mergeDirectives } from './directives.js'; import { compareNodes } from '@graphql-tools/utils'; @@ -25,7 +25,8 @@ export function mergeFields( type: { name: NameNode }, f1: ReadonlyArray | undefined, f2: ReadonlyArray | undefined, - config?: Config + config?: Config, + directives?: Record ): T[] { const result: T[] = []; if (f2 != null) { @@ -41,7 +42,7 @@ export function mergeFields( preventConflicts(type, existing, field, config?.throwOnConflict); newField.arguments = mergeArguments(field['arguments'] || [], existing['arguments'] || [], config); - newField.directives = mergeDirectives(field.directives, existing.directives, config); + newField.directives = mergeDirectives(field.directives, existing.directives, config, directives); newField.description = field.description || existing.description; result[existingIndex] = newField; } else { diff --git a/packages/merge/src/typedefs-mergers/input-type.ts b/packages/merge/src/typedefs-mergers/input-type.ts index 25ebd135e39..45ae6486650 100644 --- a/packages/merge/src/typedefs-mergers/input-type.ts +++ b/packages/merge/src/typedefs-mergers/input-type.ts @@ -1,12 +1,19 @@ import { Config } from './merge-typedefs.js'; -import { InputObjectTypeDefinitionNode, InputValueDefinitionNode, InputObjectTypeExtensionNode, Kind } from 'graphql'; +import { + InputObjectTypeDefinitionNode, + InputValueDefinitionNode, + InputObjectTypeExtensionNode, + Kind, + DirectiveDefinitionNode, +} from 'graphql'; import { mergeFields } from './fields.js'; import { mergeDirectives } from './directives.js'; export function mergeInputType( node: InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode, existingNode: InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode, - config?: Config + config?: Config, + directives?: Record ): InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode { if (existingNode) { try { @@ -21,7 +28,7 @@ export function mergeInputType( : 'InputObjectTypeExtension', loc: node.loc, fields: mergeFields(node, node.fields, existingNode.fields, config), - directives: mergeDirectives(node.directives, existingNode.directives, config), + directives: mergeDirectives(node.directives, existingNode.directives, config, directives), } as any; } catch (e: any) { throw new Error(`Unable to merge GraphQL input type "${node.name.value}": ${e.message}`); diff --git a/packages/merge/src/typedefs-mergers/interface.ts b/packages/merge/src/typedefs-mergers/interface.ts index c827273a32b..7d08ef2189a 100644 --- a/packages/merge/src/typedefs-mergers/interface.ts +++ b/packages/merge/src/typedefs-mergers/interface.ts @@ -1,5 +1,5 @@ import { Config } from './merge-typedefs.js'; -import { InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, Kind } from 'graphql'; +import { DirectiveDefinitionNode, InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, Kind } from 'graphql'; import { mergeFields } from './fields.js'; import { mergeDirectives } from './directives.js'; import { mergeNamedTypeArray } from './merge-named-type-array.js'; @@ -7,7 +7,8 @@ import { mergeNamedTypeArray } from './merge-named-type-array.js'; export function mergeInterface( node: InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode, existingNode: InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode, - config?: Config + config?: Config, + directives?: Record ): InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode { if (existingNode) { try { @@ -22,7 +23,7 @@ export function mergeInterface( : 'InterfaceTypeExtension', loc: node.loc, fields: mergeFields(node, node.fields, existingNode.fields, config), - directives: mergeDirectives(node.directives, existingNode.directives, config), + directives: mergeDirectives(node.directives, existingNode.directives, config, directives), interfaces: node['interfaces'] ? mergeNamedTypeArray(node['interfaces'], existingNode['interfaces'], config) : undefined, diff --git a/packages/merge/src/typedefs-mergers/merge-nodes.ts b/packages/merge/src/typedefs-mergers/merge-nodes.ts index f2989aa154b..bca233efece 100644 --- a/packages/merge/src/typedefs-mergers/merge-nodes.ts +++ b/packages/merge/src/typedefs-mergers/merge-nodes.ts @@ -1,5 +1,5 @@ import { Config } from './merge-typedefs.js'; -import { DefinitionNode, Kind, SchemaDefinitionNode, SchemaExtensionNode } from 'graphql'; +import { DefinitionNode, DirectiveDefinitionNode, Kind, SchemaDefinitionNode, SchemaExtensionNode } from 'graphql'; import { mergeType } from './type.js'; import { mergeEnum } from './enum.js'; import { mergeScalar } from './scalar.js'; @@ -20,8 +20,12 @@ export function isNamedDefinitionNode(definitionNode: DefinitionNode): definitio return 'name' in definitionNode; } -export function mergeGraphQLNodes(nodes: ReadonlyArray, config?: Config): MergedResultMap { - const mergedResultMap = {} as MergedResultMap; +export function mergeGraphQLNodes( + nodes: ReadonlyArray, + config?: Config, + directives: Record = {} +): MergedResultMap { + const mergedResultMap = directives as MergedResultMap; for (const nodeDefinition of nodes) { if (isNamedDefinitionNode(nodeDefinition)) { const name = nodeDefinition.name?.value; @@ -39,27 +43,27 @@ export function mergeGraphQLNodes(nodes: ReadonlyArray, config?: switch (nodeDefinition.kind) { case Kind.OBJECT_TYPE_DEFINITION: case Kind.OBJECT_TYPE_EXTENSION: - mergedResultMap[name] = mergeType(nodeDefinition, mergedResultMap[name] as any, config); + mergedResultMap[name] = mergeType(nodeDefinition, mergedResultMap[name] as any, config, directives); break; case Kind.ENUM_TYPE_DEFINITION: case Kind.ENUM_TYPE_EXTENSION: - mergedResultMap[name] = mergeEnum(nodeDefinition, mergedResultMap[name] as any, config); + mergedResultMap[name] = mergeEnum(nodeDefinition, mergedResultMap[name] as any, config, directives); break; case Kind.UNION_TYPE_DEFINITION: case Kind.UNION_TYPE_EXTENSION: - mergedResultMap[name] = mergeUnion(nodeDefinition, mergedResultMap[name] as any, config); + mergedResultMap[name] = mergeUnion(nodeDefinition, mergedResultMap[name] as any, config, directives); break; case Kind.SCALAR_TYPE_DEFINITION: case Kind.SCALAR_TYPE_EXTENSION: - mergedResultMap[name] = mergeScalar(nodeDefinition, mergedResultMap[name] as any, config); + mergedResultMap[name] = mergeScalar(nodeDefinition, mergedResultMap[name] as any, config, directives); break; case Kind.INPUT_OBJECT_TYPE_DEFINITION: case Kind.INPUT_OBJECT_TYPE_EXTENSION: - mergedResultMap[name] = mergeInputType(nodeDefinition, mergedResultMap[name] as any, config); + mergedResultMap[name] = mergeInputType(nodeDefinition, mergedResultMap[name] as any, config, directives); break; case Kind.INTERFACE_TYPE_DEFINITION: case Kind.INTERFACE_TYPE_EXTENSION: - mergedResultMap[name] = mergeInterface(nodeDefinition, mergedResultMap[name] as any, config); + mergedResultMap[name] = mergeInterface(nodeDefinition, mergedResultMap[name] as any, config, directives); break; case Kind.DIRECTIVE_DEFINITION: mergedResultMap[name] = mergeDirective(nodeDefinition, mergedResultMap[name] as any); diff --git a/packages/merge/src/typedefs-mergers/merge-typedefs.ts b/packages/merge/src/typedefs-mergers/merge-typedefs.ts index 8f40236d396..12c618081f2 100644 --- a/packages/merge/src/typedefs-mergers/merge-typedefs.ts +++ b/packages/merge/src/typedefs-mergers/merge-typedefs.ts @@ -8,9 +8,10 @@ import { OperationTypeNode, isDefinitionNode, ParseOptions, + DirectiveDefinitionNode, } from 'graphql'; import { CompareFn, defaultStringComparator, isSourceTypes, isStringTypes } from './utils.js'; -import { MergedResultMap, mergeGraphQLNodes, schemaDefSymbol } from './merge-nodes.js'; +import { mergeGraphQLNodes, schemaDefSymbol } from './merge-nodes.js'; import { getDocumentNodeFromSchema, GetDocumentNodeFromSchemaOptions, @@ -135,40 +136,65 @@ export function mergeTypeDefs(typeSource: TypeSource, config?: Partial): function visitTypeSources( typeSource: TypeSource, options: ParseOptions & GetDocumentNodeFromSchemaOptions, + allDirectives: DirectiveDefinitionNode[] = [], allNodes: DefinitionNode[] = [], visitedTypeSources = new Set() ) { if (typeSource && !visitedTypeSources.has(typeSource)) { visitedTypeSources.add(typeSource); if (typeof typeSource === 'function') { - visitTypeSources(typeSource(), options, allNodes, visitedTypeSources); + visitTypeSources(typeSource(), options, allDirectives, allNodes, visitedTypeSources); } else if (Array.isArray(typeSource)) { for (const type of typeSource) { - visitTypeSources(type, options, allNodes, visitedTypeSources); + visitTypeSources(type, options, allDirectives, allNodes, visitedTypeSources); } } else if (isSchema(typeSource)) { const documentNode = getDocumentNodeFromSchema(typeSource, options); - visitTypeSources(documentNode.definitions as DefinitionNode[], options, allNodes, visitedTypeSources); + visitTypeSources( + documentNode.definitions as DefinitionNode[], + options, + allDirectives, + allNodes, + visitedTypeSources + ); } else if (isStringTypes(typeSource) || isSourceTypes(typeSource)) { const documentNode = parse(typeSource, options); - visitTypeSources(documentNode.definitions as DefinitionNode[], options, allNodes, visitedTypeSources); + visitTypeSources( + documentNode.definitions as DefinitionNode[], + options, + allDirectives, + allNodes, + visitedTypeSources + ); } else if (typeof typeSource === 'object' && isDefinitionNode(typeSource)) { - allNodes.push(typeSource); + if (typeSource.kind === Kind.DIRECTIVE_DEFINITION) { + allDirectives.push(typeSource); + } else { + allNodes.push(typeSource); + } } else if (isDocumentNode(typeSource)) { - visitTypeSources(typeSource.definitions as DefinitionNode[], options, allNodes, visitedTypeSources); + visitTypeSources( + typeSource.definitions as DefinitionNode[], + options, + allDirectives, + allNodes, + visitedTypeSources + ); } else { throw new Error(`typeDefs must contain only strings, documents, schemas, or functions, got ${typeof typeSource}`); } } - return allNodes; + return { allDirectives, allNodes }; } export function mergeGraphQLTypes(typeSource: TypeSource, config: Config): DefinitionNode[] { resetComments(); - const allNodes = visitTypeSources(typeSource, config); + const { allDirectives, allNodes } = visitTypeSources(typeSource, config); + + const mergedDirectives = mergeGraphQLNodes(allDirectives, config) as Record; - const mergedNodes: MergedResultMap = mergeGraphQLNodes(allNodes, config); + const mergedNodes = mergeGraphQLNodes(allNodes, config, mergedDirectives); if (config?.useSchemaDefinition) { // XXX: right now we don't handle multiple schema definitions diff --git a/packages/merge/src/typedefs-mergers/scalar.ts b/packages/merge/src/typedefs-mergers/scalar.ts index cdfeabb5156..411478a8dc9 100644 --- a/packages/merge/src/typedefs-mergers/scalar.ts +++ b/packages/merge/src/typedefs-mergers/scalar.ts @@ -1,11 +1,12 @@ -import { Kind, ScalarTypeDefinitionNode, ScalarTypeExtensionNode } from 'graphql'; +import { DirectiveDefinitionNode, Kind, ScalarTypeDefinitionNode, ScalarTypeExtensionNode } from 'graphql'; import { mergeDirectives } from './directives.js'; import { Config } from './merge-typedefs.js'; export function mergeScalar( node: ScalarTypeDefinitionNode | ScalarTypeExtensionNode, existingNode: ScalarTypeDefinitionNode | ScalarTypeExtensionNode, - config?: Config + config?: Config, + directives?: Record ): ScalarTypeDefinitionNode | ScalarTypeExtensionNode { if (existingNode) { return { @@ -18,7 +19,7 @@ export function mergeScalar( ? 'ScalarTypeDefinition' : 'ScalarTypeExtension', loc: node.loc, - directives: mergeDirectives(node.directives, existingNode.directives, config), + directives: mergeDirectives(node.directives, existingNode.directives, config, directives), } as any; } diff --git a/packages/merge/src/typedefs-mergers/schema-def.ts b/packages/merge/src/typedefs-mergers/schema-def.ts index 6c82d099d1d..f9856d98394 100644 --- a/packages/merge/src/typedefs-mergers/schema-def.ts +++ b/packages/merge/src/typedefs-mergers/schema-def.ts @@ -1,4 +1,10 @@ -import { Kind, OperationTypeDefinitionNode, SchemaDefinitionNode, SchemaExtensionNode } from 'graphql'; +import { + DirectiveDefinitionNode, + Kind, + OperationTypeDefinitionNode, + SchemaDefinitionNode, + SchemaExtensionNode, +} from 'graphql'; import { mergeDirectives } from './directives.js'; import { Config } from './merge-typedefs.js'; @@ -26,7 +32,8 @@ function mergeOperationTypes( export function mergeSchemaDefs( node: SchemaDefinitionNode | SchemaExtensionNode, existingNode: SchemaDefinitionNode | SchemaExtensionNode, - config?: Config + config?: Config, + directives?: Record ): SchemaDefinitionNode | SchemaExtensionNode { if (existingNode) { return { @@ -35,7 +42,7 @@ export function mergeSchemaDefs( ? Kind.SCHEMA_DEFINITION : Kind.SCHEMA_EXTENSION, description: node['description'] || existingNode['description'], - directives: mergeDirectives(node.directives, existingNode.directives, config), + directives: mergeDirectives(node.directives, existingNode.directives, config, directives), operationTypes: mergeOperationTypes(node.operationTypes, existingNode.operationTypes), } as any; } diff --git a/packages/merge/src/typedefs-mergers/type.ts b/packages/merge/src/typedefs-mergers/type.ts index 5ae2d58f8b4..a0236a53dc0 100644 --- a/packages/merge/src/typedefs-mergers/type.ts +++ b/packages/merge/src/typedefs-mergers/type.ts @@ -1,5 +1,5 @@ import { Config } from './merge-typedefs.js'; -import { Kind, ObjectTypeDefinitionNode, ObjectTypeExtensionNode } from 'graphql'; +import { DirectiveDefinitionNode, Kind, ObjectTypeDefinitionNode, ObjectTypeExtensionNode } from 'graphql'; import { mergeFields } from './fields.js'; import { mergeDirectives } from './directives.js'; import { mergeNamedTypeArray } from './merge-named-type-array.js'; @@ -7,7 +7,8 @@ import { mergeNamedTypeArray } from './merge-named-type-array.js'; export function mergeType( node: ObjectTypeDefinitionNode | ObjectTypeExtensionNode, existingNode: ObjectTypeDefinitionNode | ObjectTypeExtensionNode, - config?: Config + config?: Config, + directives?: Record ): ObjectTypeDefinitionNode | ObjectTypeExtensionNode { if (existingNode) { try { @@ -22,7 +23,7 @@ export function mergeType( : 'ObjectTypeExtension', loc: node.loc, fields: mergeFields(node, node.fields, existingNode.fields, config), - directives: mergeDirectives(node.directives, existingNode.directives, config), + directives: mergeDirectives(node.directives, existingNode.directives, config, directives), interfaces: mergeNamedTypeArray(node.interfaces, existingNode.interfaces, config), } as any; } catch (e: any) { diff --git a/packages/merge/src/typedefs-mergers/union.ts b/packages/merge/src/typedefs-mergers/union.ts index 3e08f92b390..237cd6a4264 100644 --- a/packages/merge/src/typedefs-mergers/union.ts +++ b/packages/merge/src/typedefs-mergers/union.ts @@ -1,4 +1,4 @@ -import { Kind, UnionTypeDefinitionNode, UnionTypeExtensionNode } from 'graphql'; +import { DirectiveDefinitionNode, Kind, UnionTypeDefinitionNode, UnionTypeExtensionNode } from 'graphql'; import { mergeDirectives } from './directives.js'; import { mergeNamedTypeArray } from './merge-named-type-array.js'; import { Config } from './merge-typedefs.js'; @@ -6,14 +6,15 @@ import { Config } from './merge-typedefs.js'; export function mergeUnion( first: UnionTypeDefinitionNode | UnionTypeExtensionNode, second: UnionTypeDefinitionNode | UnionTypeExtensionNode, - config?: Config + config?: Config, + directives?: Record ): UnionTypeDefinitionNode | UnionTypeExtensionNode { if (second) { return { name: first.name, description: first['description'] || second['description'], // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility - directives: mergeDirectives(first.directives, second.directives, config) as any, + directives: mergeDirectives(first.directives, second.directives, config, directives) as any, kind: config?.convertExtensions || first.kind === 'UnionTypeDefinition' || second.kind === 'UnionTypeDefinition' ? Kind.UNION_TYPE_DEFINITION diff --git a/packages/merge/tests/merge-typedefs.spec.ts b/packages/merge/tests/merge-typedefs.spec.ts index 8ddf8557bc9..9e8e50de5c1 100644 --- a/packages/merge/tests/merge-typedefs.spec.ts +++ b/packages/merge/tests/merge-typedefs.spec.ts @@ -328,9 +328,9 @@ describe('Merge TypeDefs', () => { expect(stripWhitespaces(print(merged))).toBe( stripWhitespaces(/* GraphQL */ ` - scalar JSON @sqlType(type: "json") - directive @sqlType(type: String!) on SCALAR + + scalar JSON @sqlType(type: "json") `) ); }); @@ -475,6 +475,52 @@ describe('Merge TypeDefs', () => { ); }); + it('should merge repeatable directives', () => { + const merged = mergeTypeDefs([ + ` + directive @fields(name: String!, args: [String]) repeatable on INTERFACE + type CoreType + @fields(name: "id") + @fields(name: "name") + { id: Int, name: String } + `, + `type MyType { id: Int }`, + `type MyType @fields(name: "id") { id: Int }`, + `type MyType @fields(name: "id", args: ["1"]) { id: Int }`, + `type MyType @fields(name: "id", args: ["2"]) { id: Int }`, + `type MyType @fields(name: "name") { name: String }`, + `type Query { f1: MyType }`, + ]); + + expect(stripWhitespaces(print(merged))).toBe( + stripWhitespaces(/* GraphQL */ ` + directive @fields(name: String!, args: [String]) repeatable on INTERFACE + + type CoreType @fields(name: "id") @fields(name: "name") { + id: Int + name: String + } + + type MyType + @fields(name: "id") + @fields(name: "id", args: ["1"]) + @fields(name: "id", args: ["2"]) + @fields(name: "name") { + id: Int + name: String + } + + type Query { + f1: MyType + } + + schema { + query: Query + } + `) + ); + }); + it('should fail if inputs of the same directive are different from each other', (done: jest.DoneCallback) => { try { mergeTypeDefs([