From 04dab42d1afd9c778b21ea9d8d706738aebd89a8 Mon Sep 17 00:00:00 2001 From: Andrew Ovens <107420510+aovens-quantifi@users.noreply.github.com> Date: Sat, 4 Oct 2025 06:29:58 -0600 Subject: [PATCH 1/2] feat(typed-document-node): Allow importing operation types --- .../typed-document-node/src/config.ts | 27 ++++++++++++++ .../typed-document-node/src/visitor.ts | 8 +++-- .../tests/typed-document-node.spec.ts | 35 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/plugins/typescript/typed-document-node/src/config.ts b/packages/plugins/typescript/typed-document-node/src/config.ts index a4de17d9ec2..5dd1d16eb96 100644 --- a/packages/plugins/typescript/typed-document-node/src/config.ts +++ b/packages/plugins/typescript/typed-document-node/src/config.ts @@ -48,4 +48,31 @@ export interface TypeScriptTypedDocumentNodesConfig extends RawClientSideBasePlu * ``` */ addTypenameToSelectionSets?: boolean; + + /** + * @description Allows you to import the operation types from a different file. + * @default "" + * + * @exampleMarkdown + * ```ts filename="codegen.ts" + * import type { CodegenConfig } from '@graphql-codegen/cli'; + * + * const config: CodegenConfig = { + * // ... + * generates: { + * 'path/to/file.ts': { + * plugins: ['typescript', 'typescript-operations'] + * }, + * 'path/to/file2.ts': { + * plugins: ['typed-document-node'], + * config: { + * importOperationTypesFrom: 'path/to/file.ts' + * }, + * }, + * }, + * }; + * export default config; + * ``` + */ + importOperationTypesFrom?: string; } diff --git a/packages/plugins/typescript/typed-document-node/src/visitor.ts b/packages/plugins/typescript/typed-document-node/src/visitor.ts index 343c1d3ffe5..ea4896a3891 100644 --- a/packages/plugins/typescript/typed-document-node/src/visitor.ts +++ b/packages/plugins/typescript/typed-document-node/src/visitor.ts @@ -103,11 +103,15 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor< this.config.documentMode === DocumentMode.documentNodeImportFragments || this.config.documentMode === DocumentMode.graphQLTag ) { - return ` as unknown as DocumentNode<${resultType}, ${variablesTypes}>`; + return ` as unknown as DocumentNode<${this.config.importOperationTypesFrom ? 'Types.' : ''}${resultType}, ${ + this.config.importOperationTypesFrom ? 'Types.' : '' + }${variablesTypes}>`; } if (this.config.documentMode === DocumentMode.string) { - return ` as unknown as TypedDocumentString<${resultType}, ${variablesTypes}>`; + return ` as unknown as TypedDocumentString<${ + this.config.importOperationTypesFrom ? 'Types.' : '' + }${resultType}, ${this.config.importOperationTypesFrom ? 'Types.' : ''}${variablesTypes}>`; } return super.getDocumentNodeSignature(resultType, variablesTypes, node); diff --git a/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts b/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts index 6cf6ef5a35d..1cc3ab772f8 100644 --- a/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts +++ b/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts @@ -77,4 +77,39 @@ describe('TypedDocumentNode', () => { expect((res.content.match(/__typename/g) || []).length).toBe(1); }); }); + + describe('addTypenameToSelectionSets', () => { + it('Should import Types from the given file', async () => { + const schema = buildSchema(/* GraphQL */ ` + schema { + query: Query + } + + type Query { + job: Job + } + + type Job { + id: ID! + } + `); + + const ast = parse(/* GraphQL */ ` + query { + job { + id + } + } + `); + + const res = (await plugin( + schema, + [{ location: '', document: ast }], + { importOperationTypesFrom: 'file.ts' }, + { outputFile: '' } + )) as Types.ComplexPluginOutput; + + expect((res.content.match(//g) || []).length).toBe(1); + }); + }); }); From e4c6a0b73eb15d3e94ba7575027029675da2c203 Mon Sep 17 00:00:00 2001 From: Andrew Ovens <107420510+aovens-quantifi@users.noreply.github.com> Date: Mon, 6 Oct 2025 07:07:33 -0600 Subject: [PATCH 2/2] fix(typed-document-node): Handle fragments with operations import --- .../typescript/typed-document-node/src/visitor.ts | 11 +++++------ .../tests/typed-document-node.spec.ts | 7 ++++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/plugins/typescript/typed-document-node/src/visitor.ts b/packages/plugins/typescript/typed-document-node/src/visitor.ts index ea4896a3891..dc090ffb9aa 100644 --- a/packages/plugins/typescript/typed-document-node/src/visitor.ts +++ b/packages/plugins/typescript/typed-document-node/src/visitor.ts @@ -98,20 +98,19 @@ export class TypeScriptDocumentNodesVisitor extends ClientSideBaseVisitor< } protected getDocumentNodeSignature(resultType: string, variablesTypes: string, node) { + const shouldUseImportPrefix = !!this.config.importOperationTypesFrom; + const resultImportPrefix = shouldUseImportPrefix && resultType !== 'unknown' ? 'Types.' : ''; + const variablesImportPrefix = shouldUseImportPrefix && variablesTypes !== 'unknown' ? 'Types.' : ''; if ( this.config.documentMode === DocumentMode.documentNode || this.config.documentMode === DocumentMode.documentNodeImportFragments || this.config.documentMode === DocumentMode.graphQLTag ) { - return ` as unknown as DocumentNode<${this.config.importOperationTypesFrom ? 'Types.' : ''}${resultType}, ${ - this.config.importOperationTypesFrom ? 'Types.' : '' - }${variablesTypes}>`; + return ` as unknown as DocumentNode<${resultImportPrefix}${resultType}, ${variablesImportPrefix}${variablesTypes}>`; } if (this.config.documentMode === DocumentMode.string) { - return ` as unknown as TypedDocumentString<${ - this.config.importOperationTypesFrom ? 'Types.' : '' - }${resultType}, ${this.config.importOperationTypesFrom ? 'Types.' : ''}${variablesTypes}>`; + return ` as unknown as TypedDocumentString<${resultImportPrefix}${resultType}, ${variablesImportPrefix}${variablesTypes}>`; } return super.getDocumentNodeSignature(resultType, variablesTypes, node); diff --git a/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts b/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts index 1cc3ab772f8..42764efdc45 100644 --- a/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts +++ b/packages/plugins/typescript/typed-document-node/tests/typed-document-node.spec.ts @@ -97,9 +97,13 @@ describe('TypedDocumentNode', () => { const ast = parse(/* GraphQL */ ` query { job { - id + ...JobFragment } } + + fragment JobFragment on Job { + id + } `); const res = (await plugin( @@ -110,6 +114,7 @@ describe('TypedDocumentNode', () => { )) as Types.ComplexPluginOutput; expect((res.content.match(//g) || []).length).toBe(1); + expect((res.content.match(//g) || []).length).toBe(1); }); }); });