From 3a7bd844315cfbb99305cec6f856178299ab5a31 Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Tue, 25 Jul 2023 12:52:31 +0200 Subject: [PATCH] feat: allow deferredFields works with fragment --- .../src/client-side-base-visitor.ts | 22 +++++--- .../client/tests/client-preset.spec.ts | 50 +++++++++++++++++++ .../tests/fixtures/with-deferred-fragment.ts | 8 +++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/packages/plugins/other/visitor-plugin-common/src/client-side-base-visitor.ts b/packages/plugins/other/visitor-plugin-common/src/client-side-base-visitor.ts index 5dbad1f4536..efd0318cd75 100644 --- a/packages/plugins/other/visitor-plugin-common/src/client-side-base-visitor.ts +++ b/packages/plugins/other/visitor-plugin-common/src/client-side-base-visitor.ts @@ -411,7 +411,7 @@ export class ClientSideBaseVisitor< } let metaString = ''; - if (this._onExecutableDocumentNode && node.kind === Kind.OPERATION_DEFINITION) { + if (this._onExecutableDocumentNode) { const meta = this._getGraphQLCodegenMetadata(node, definitions); if (meta) { @@ -428,7 +428,9 @@ export class ClientSideBaseVisitor< if (this.config.documentMode === DocumentMode.string) { if (node.kind === Kind.FRAGMENT_DEFINITION) { - return `new TypedDocumentString(\`${doc}\`, ${JSON.stringify({ fragmentName: node.name.value })})`; + const meta = this._getGraphQLCodegenMetadata(node, gqlTag([doc]).definitions); + + return `new TypedDocumentString(\`${doc}\`, ${JSON.stringify({ fragmentName: node.name.value, ...meta })})`; } if (this._onExecutableDocumentNode && node.kind === Kind.OPERATION_DEFINITION) { @@ -451,15 +453,17 @@ export class ClientSideBaseVisitor< } protected _getGraphQLCodegenMetadata( - node: OperationDefinitionNode, + node: OperationDefinitionNode | FragmentDefinitionNode, definitions?: ReadonlyArray ): Record | void | undefined { let meta: Record | void | undefined; - meta = this._onExecutableDocumentNode({ - kind: Kind.DOCUMENT, - definitions, - }); + if (node.kind === Kind.OPERATION_DEFINITION) { + meta = this._onExecutableDocumentNode({ + kind: Kind.DOCUMENT, + definitions, + }); + } const deferredFields = this._findDeferredFields(node); if (Object.keys(deferredFields).length) { @@ -472,7 +476,9 @@ export class ClientSideBaseVisitor< return meta; } - protected _findDeferredFields(node: OperationDefinitionNode): { [fargmentName: string]: string[] } { + protected _findDeferredFields(node: OperationDefinitionNode | FragmentDefinitionNode): { + [fargmentName: string]: string[]; + } { const deferredFields: { [fargmentName: string]: string[] } = {}; const queue: SelectionNode[] = [...node.selectionSet.selections]; while (queue.length) { diff --git a/packages/presets/client/tests/client-preset.spec.ts b/packages/presets/client/tests/client-preset.spec.ts index fdf897883b5..ed9560aa81a 100644 --- a/packages/presets/client/tests/client-preset.spec.ts +++ b/packages/presets/client/tests/client-preset.spec.ts @@ -1868,8 +1868,14 @@ export * from "./gql.js";`); export type FooFragment = { __typename?: 'Foo', id?: string | null } & ({ __typename?: 'Foo', value?: string | null } | { __typename?: 'Foo', value?: never }) & { ' $fragmentName'?: 'FooFragment' }; + export type FooNestedFragment = { __typename?: 'Foo', id?: string | null } & ( + { __typename?: 'Foo' } + & { ' $fragmentRefs'?: { 'FooFragment': Incremental } } + ) & { ' $fragmentName'?: 'FooNestedFragment' }; + export const FooFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; export const FooFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]} as unknown as DocumentNode; + export const FooNestedFragmentDoc = {"__meta__":{"deferredFields":{"foo":["id"]}},"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"fooNested"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"foo"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]} as unknown as DocumentNode; export const FooDocument = {"__meta__":{"deferredFields":{"Foo":["value"]}},"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Foo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"foo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; export const FoosDocument = {"__meta__":{"deferredFields":{"Foo":["value"]}},"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Foos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"foos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode;" `); @@ -1953,8 +1959,14 @@ export * from "./gql.js";`); export type FooFragment = { __typename?: 'Foo', id?: string | null } & ({ __typename?: 'Foo', value?: string | null } | { __typename?: 'Foo', value?: never }) & { ' $fragmentName'?: 'FooFragment' }; + export type FooNestedFragment = { __typename?: 'Foo', id?: string | null } & ( + { __typename?: 'Foo' } + & { ' $fragmentRefs'?: { 'FooFragment': Incremental } } + ) & { ' $fragmentName'?: 'FooNestedFragment' }; + export const FooFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; export const FooFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]} as unknown as DocumentNode; + export const FooNestedFragmentDoc = {"__meta__":{"deferredFields":{"foo":["id"]}},"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"fooNested"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"foo"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}]}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]} as unknown as DocumentNode; export const FooDocument = {"__meta__":{"hash":"39c47d2da0fb0e6867abbe2ec942d9858f2d76c7","deferredFields":{"Foo":["value"]}},"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Foo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"foo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode; export const FoosDocument = {"__meta__":{"hash":"8aba765173b2302b9857334e9959d97a2168dbc8","deferredFields":{"Foo":["value"]}},"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Foos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"foos"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"Foo"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"defer"}}]}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"Foo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Foo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]} as unknown as DocumentNode;" `); @@ -2038,6 +2050,11 @@ export * from "./gql.js";`); export type FooFragment = { __typename?: 'Foo', id?: string | null } & ({ __typename?: 'Foo', value?: string | null } | { __typename?: 'Foo', value?: never }) & { ' $fragmentName'?: 'FooFragment' }; + export type FooNestedFragment = { __typename?: 'Foo', id?: string | null } & ( + { __typename?: 'Foo' } + & { ' $fragmentRefs'?: { 'FooFragment': Incremental } } + ) & { ' $fragmentName'?: 'FooNestedFragment' }; + export class TypedDocumentString extends String implements DocumentTypeDecoration @@ -2065,6 +2082,20 @@ export * from "./gql.js";`); } } \`, {"fragmentName":"foo"}) as unknown as TypedDocumentString; + export const FooNestedFragmentDoc = new TypedDocumentString(\` + fragment fooNested on Foo { + id + ...foo @defer + } + fragment Foo on Foo { + value + } + fragment foo on Foo { + id + ... on Foo @defer { + value + } + }\`, {"fragmentName":"fooNested","deferredFields":{"foo":["id"]}}) as unknown as TypedDocumentString; export const FooDocument = new TypedDocumentString(\` query Foo { foo { @@ -2179,6 +2210,11 @@ export * from "./gql.js";`); export type FooFragment = { __typename?: 'Foo', id?: string | null } & ({ __typename?: 'Foo', value?: string | null } | { __typename?: 'Foo', value?: never }) & { ' $fragmentName'?: 'FooFragment' }; + export type FooNestedFragment = { __typename?: 'Foo', id?: string | null } & ( + { __typename?: 'Foo' } + & { ' $fragmentRefs'?: { 'FooFragment': Incremental } } + ) & { ' $fragmentName'?: 'FooNestedFragment' }; + export class TypedDocumentString extends String implements DocumentTypeDecoration @@ -2206,6 +2242,20 @@ export * from "./gql.js";`); } } \`, {"fragmentName":"foo"}) as unknown as TypedDocumentString; + export const FooNestedFragmentDoc = new TypedDocumentString(\` + fragment fooNested on Foo { + id + ...foo @defer + } + fragment Foo on Foo { + value + } + fragment foo on Foo { + id + ... on Foo @defer { + value + } + }\`, {"fragmentName":"fooNested","deferredFields":{"foo":["id"]}}) as unknown as TypedDocumentString; export const FooDocument = new TypedDocumentString(\` query Foo { foo { diff --git a/packages/presets/client/tests/fixtures/with-deferred-fragment.ts b/packages/presets/client/tests/fixtures/with-deferred-fragment.ts index fea2ea24c1f..d228a9638e3 100644 --- a/packages/presets/client/tests/fixtures/with-deferred-fragment.ts +++ b/packages/presets/client/tests/fixtures/with-deferred-fragment.ts @@ -34,3 +34,11 @@ const NestedFragment = gql(/* GraphQL */ ` } } `); + +//@ts-ignore +const NestedFragmentWithFragment = gql(/* GraphQL */ ` + fragment fooNested on Foo { + id + ...foo @defer + } +`);