From ac977878f3216220aae031f87727fe9e9af22369 Mon Sep 17 00:00:00 2001 From: ifeanyi-ugwu Date: Thu, 9 Jul 2026 13:26:28 +0300 Subject: [PATCH] feat(visitor-plugin-common): support object form for the codegenScalarType extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read the `codegenScalarType` scalar extension through the existing `normalizeScalarType` helper, so it accepts either a string (mapped to both the input and output slots, unchanged) or `{ input, output }` to map the two positions independently — matching what the `scalars` config option already supports. This lets a schema-first scalar declare a stricter parsed/input type than its accepted resolver-return/output type, instead of collapsing both into one. --- .changeset/codegenscalartype-input-output.md | 7 ++++ .../other/visitor-plugin-common/src/utils.ts | 7 ++-- .../typescript/tests/typescript.spec.ts | 35 ++++++++++++++++++- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 .changeset/codegenscalartype-input-output.md diff --git a/.changeset/codegenscalartype-input-output.md b/.changeset/codegenscalartype-input-output.md new file mode 100644 index 00000000000..b40f852d331 --- /dev/null +++ b/.changeset/codegenscalartype-input-output.md @@ -0,0 +1,7 @@ +--- +'@graphql-codegen/visitor-plugin-common': minor +--- + +Support an object form for the `codegenScalarType` scalar extension. + +A scalar's `extensions.codegenScalarType` can now be either a string (applied to both the `input` and `output` types, as before) or `{ input, output }` to map the two positions independently — matching what the `scalars` config option already accepts. This lets schema-first scalar libraries declare a stricter parsed/input type than the accepted resolver-return/output type directly from the schema, instead of collapsing both into one type. diff --git a/packages/plugins/other/visitor-plugin-common/src/utils.ts b/packages/plugins/other/visitor-plugin-common/src/utils.ts index bdfb7340423..120eb3a1103 100644 --- a/packages/plugins/other/visitor-plugin-common/src/utils.ts +++ b/packages/plugins/other/visitor-plugin-common/src/utils.ts @@ -366,14 +366,17 @@ export function buildScalars( }; } } else if (scalarType.extensions?.codegenScalarType) { + const normalizedScalar = normalizeScalarType( + scalarType.extensions.codegenScalarType as string | { input: string; output: string }, + ); result[name] = { input: { isExternal: false, - type: scalarType.extensions.codegenScalarType as string, + type: normalizedScalar.input, }, output: { isExternal: false, - type: scalarType.extensions.codegenScalarType as string, + type: normalizedScalar.output, }, }; } else if (!defaultScalarsMapping[name]) { diff --git a/packages/plugins/typescript/typescript/tests/typescript.spec.ts b/packages/plugins/typescript/typescript/tests/typescript.spec.ts index 4417d547e46..ee49124d422 100644 --- a/packages/plugins/typescript/typescript/tests/typescript.spec.ts +++ b/packages/plugins/typescript/typescript/tests/typescript.spec.ts @@ -1,4 +1,11 @@ -import { buildSchema, GraphQLEnumType, GraphQLObjectType, GraphQLSchema, parse } from 'graphql'; +import { + buildSchema, + GraphQLEnumType, + GraphQLObjectType, + GraphQLScalarType, + GraphQLSchema, + parse, +} from 'graphql'; import { mergeOutputs, Types } from '@graphql-codegen/plugin-helpers'; import { validateTs } from '@graphql-codegen/testing'; import { plugin } from '../src/index.js'; @@ -12,6 +19,32 @@ describe('TypeScript', () => { expect(result.prepend).toBeSimilarStringTo('export type Maybe ='); }); + describe('codegenScalarType extension', () => { + const schemaWithScalarExtension = (codegenScalarType: unknown) => + new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + a: { + type: new GraphQLScalarType({ name: 'MyScalar', extensions: { codegenScalarType } }), + }, + }, + }), + }); + + it('maps a string codegenScalarType to both input and output', async () => { + const schema = schemaWithScalarExtension('MyType'); + const result = await plugin(schema, [], {}, { outputFile: '' }); + expect(result.content).toBeSimilarStringTo(`MyScalar: { input: MyType; output: MyType; }`); + }); + + it('maps an object codegenScalarType to distinct input and output', async () => { + const schema = schemaWithScalarExtension({ input: 'MyInput', output: 'MyOutput' }); + const result = await plugin(schema, [], {}, { outputFile: '' }); + expect(result.content).toBeSimilarStringTo(`MyScalar: { input: MyInput; output: MyOutput; }`); + }); + }); + describe('description to comment', () => { it('Should include a description for Scalars type', async () => { const schema = buildSchema(/* GraphQL */ `