Skip to content

Commit d8566c0

Browse files
authored
[operations] CODEGEN-784 - Fix Apollo @unmask fragmentRefs (#10302)
1 parent 18d09a8 commit d8566c0

File tree

4 files changed

+29
-17
lines changed

4 files changed

+29
-17
lines changed

.changeset/funny-monkeys-hide.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': patch
3+
'@graphql-codegen/typescript-operations': patch
4+
'@graphql-codegen/client-preset': patch
5+
---
6+
7+
Fix Apollo unmask directive incorrectly generating fragmentRefs

packages/plugins/other/visitor-plugin-common/src/selection-set-to-object.ts

+18-7
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ export class SelectionSetToObject<Config extends ParsedDocumentsConfig = ParsedD
548548
selectionNodes = [...selectionNodes];
549549
let inlineFragmentConditional = false;
550550
for (const selectionNode of selectionNodes) {
551+
// 1. Handle Field or Directtives selection nodes
551552
if ('kind' in selectionNode) {
552553
if (selectionNode.kind === 'Field') {
553554
if (selectionNode.selectionSet) {
@@ -598,21 +599,31 @@ export class SelectionSetToObject<Config extends ParsedDocumentsConfig = ParsedD
598599
continue;
599600
}
600601

601-
if (this._config.inlineFragmentTypes === 'combine' || this._config.inlineFragmentTypes === 'mask') {
602-
fragmentsSpreadUsages.push(selectionNode.typeName);
602+
// 2. Handle Fragment Spread nodes
603+
// A Fragment Spread can be:
604+
// - masked: the fields declared in the Fragment do not appear in the operation types
605+
// - inline: the fields declared in the Fragment appear in the operation types
603606

604-
const isApolloUnmaskEnabled = this._config.customDirectives.apolloUnmask;
607+
// 2a. If `inlineFragmentTypes` is 'combine' or 'mask', the Fragment Spread is masked by default
608+
// In some cases, a masked node could be unmasked (i.e. treated as inline):
609+
// - Fragment spread node is marked with Apollo `@unmask`, e.g. `...User @unmask`
610+
if (this._config.inlineFragmentTypes === 'combine' || this._config.inlineFragmentTypes === 'mask') {
611+
let isMasked = true;
605612

606613
if (
607-
!isApolloUnmaskEnabled ||
608-
(isApolloUnmaskEnabled && !selectionNode.fragmentDirectives?.some(d => d.name.value === 'unmask'))
614+
this._config.customDirectives.apolloUnmask &&
615+
selectionNode.fragmentDirectives.some(d => d.name.value === 'unmask')
609616
) {
617+
isMasked = false;
618+
}
619+
620+
if (isMasked) {
621+
fragmentsSpreadUsages.push(selectionNode.typeName);
610622
continue;
611623
}
612624
}
613625

614-
// Handle Fragment Spreads by generating inline types.
615-
626+
// 2b. If the Fragment Spread is not masked, generate inline types.
616627
const fragmentType = this._schema.getType(selectionNode.onType);
617628

618629
if (fragmentType == null) {

packages/plugins/typescript/operations/tests/ts-documents.apolloUnmask.spec.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ describe('TypeScript Operations Plugin - apolloUnmask', () => {
2525
"export type Unnamed_1_QueryVariables = Exact<{ [key: string]: never; }>;
2626
2727
28-
export type Unnamed_1_Query = { __typename?: 'Query', me?: (
29-
{ __typename?: 'User', id: string }
30-
& { ' $fragmentRefs'?: { 'UserFragmentFragment': UserFragmentFragment } }
31-
) | null };
28+
export type Unnamed_1_Query = { __typename?: 'Query', me?: { __typename?: 'User', id: string } | null };
3229
3330
export type UserFragmentFragment = { __typename?: 'User', id: string } & { ' $fragmentName'?: 'UserFragmentFragment' };
3431
"
@@ -125,7 +122,7 @@ describe('TypeScript Operations Plugin - apolloUnmask', () => {
125122
126123
export type Unnamed_1_Query = { __typename?: 'Query', me?: (
127124
{ __typename?: 'User', id: string }
128-
& { ' $fragmentRefs'?: { 'UserFragmentFragment': UserFragmentFragment;'UserFragment2Fragment': UserFragment2Fragment } }
125+
& { ' $fragmentRefs'?: { 'UserFragment2Fragment': UserFragment2Fragment } }
129126
) | null };
130127
131128
export type UserFragmentFragment = { __typename?: 'User', id: string } & { ' $fragmentName'?: 'UserFragmentFragment' };
@@ -164,7 +161,7 @@ describe('TypeScript Operations Plugin - apolloUnmask', () => {
164161
165162
export type Unnamed_1_Query = { __typename?: 'Query', me?: (
166163
{ __typename?: 'User', id: string, email: string }
167-
& { ' $fragmentRefs'?: { 'UserFragmentFragment': UserFragmentFragment;'UserFragment2Fragment': UserFragment2Fragment } }
164+
& { ' $fragmentRefs'?: { 'UserFragment2Fragment': UserFragment2Fragment } }
168165
) | null };
169166
170167
export type UserFragmentFragment = { __typename?: 'User', id: string, email: string } & { ' $fragmentName'?: 'UserFragmentFragment' };

packages/presets/client/tests/client-preset.spec.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,7 @@ export * from "./gql";`);
619619
export type MeQueryVariables = Exact<{ [key: string]: never; }>;
620620
621621
622-
export type MeQuery = { __typename?: 'Query', unmasked?: (
623-
{ __typename?: 'User', id: string, name: string, age: number }
624-
& { ' $fragmentRefs'?: { 'User_MeFragment': User_MeFragment } }
625-
) | null, masked?: (
622+
export type MeQuery = { __typename?: 'Query', unmasked?: { __typename?: 'User', id: string, name: string, age: number } | null, masked?: (
626623
{ __typename?: 'User', id: string }
627624
& { ' $fragmentRefs'?: { 'User_MeFragment': User_MeFragment } }
628625
) | null };

0 commit comments

Comments
 (0)