From c33e9acac270fd15afe99285e50fecd1ca816286 Mon Sep 17 00:00:00 2001 From: Jonathan Wieben Date: Thu, 11 Jul 2024 18:14:45 +0200 Subject: [PATCH] feat(named-operations-object): add option to throw on duplicates (#775) * feat(named-operations-object): add option to throw on duplicate operation name * add changeset --- .changeset/tough-roses-divide.md | 5 ++ .../named-operations-object/src/index.ts | 15 +++++ .../tests/plugin.spec.ts | 56 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 .changeset/tough-roses-divide.md diff --git a/.changeset/tough-roses-divide.md b/.changeset/tough-roses-divide.md new file mode 100644 index 000000000..4cbf0483e --- /dev/null +++ b/.changeset/tough-roses-divide.md @@ -0,0 +1,5 @@ +--- +'@graphql-codegen/named-operations-object': minor +--- + +add option to throw on duplicate operation names diff --git a/packages/plugins/typescript/named-operations-object/src/index.ts b/packages/plugins/typescript/named-operations-object/src/index.ts index c8fe40658..400dc1954 100644 --- a/packages/plugins/typescript/named-operations-object/src/index.ts +++ b/packages/plugins/typescript/named-operations-object/src/index.ts @@ -24,6 +24,11 @@ export interface NamedOperationsObjectPluginConfig { * @default false */ useConsts?: boolean; + /** + * @description Throws an error if a duplicate operation name is found. + * @default false + */ + throwOnDuplicate?: boolean; } export const plugin: PluginFunction = ( @@ -43,10 +48,16 @@ export const plugin: PluginFunction = fragment: new Set(), }; + const duplicateOperationNames = []; + oldVisit(allAst, { enter: { OperationDefinition: node => { if (node.name?.value) { + if (allOperationsNames[node.operation].has(node.name.value)) { + duplicateOperationNames.push(node.name.value); + return; + } allOperationsNames[node.operation].add(node.name.value); } }, @@ -56,6 +67,10 @@ export const plugin: PluginFunction = }, }); + if (config.throwOnDuplicate && duplicateOperationNames.length > 0) { + throw new Error(`Duplicated operation name(s): ${duplicateOperationNames.join(', ')}`); + } + const objectItems = Object.keys(allOperationsNames) .map(operationType => { const relevantOperations: Set = allOperationsNames[operationType]; diff --git a/packages/plugins/typescript/named-operations-object/tests/plugin.spec.ts b/packages/plugins/typescript/named-operations-object/tests/plugin.spec.ts index d2e711e69..82708ffc8 100644 --- a/packages/plugins/typescript/named-operations-object/tests/plugin.spec.ts +++ b/packages/plugins/typescript/named-operations-object/tests/plugin.spec.ts @@ -223,4 +223,60 @@ describe('named-operations-object', () => { } }`); }); + + it('Should not throw on duplicate operations', async () => { + const result = await plugin( + null, + [ + { + document: parse(/* GraphQL */ ` + query myQuery { + id + } + `), + }, + { + document: parse(/* GraphQL */ ` + query myQuery { + id + } + `), + }, + ], + {}, + ); + + expect(result).toBeSimilarStringTo(`export const namedOperations = { + Query: { + myQuery: 'myQuery' + } + }`); + }); + + it('Should throw on duplicate operations when config.throwOnDuplicate is set', async () => { + expect(() => + plugin( + null, + [ + { + document: parse(/* GraphQL */ ` + query myQuery { + id + } + `), + }, + { + document: parse(/* GraphQL */ ` + query myQuery { + id + } + `), + }, + ], + { + throwOnDuplicate: true, + }, + ), + ).toThrowErrorMatchingInlineSnapshot(`"Duplicated operation name(s): myQuery"`); + }); });