From 5e594ef8f39b9e1036b6bcaa977f914a66fec03e Mon Sep 17 00:00:00 2001 From: Rachel Church Date: Thu, 13 Jun 2024 03:22:18 -0500 Subject: [PATCH] feat(client-preset): preserving Array or ReadonlyArray in useFragment() data (#9804) * Preserving Array or ReadonlyArray * adding changeset * Fixing Property 'join' does not exist on type 'string' error * update fixtures * changeset formatting --------- Co-authored-by: Laurin Quast --- .changeset/proud-cycles-design.md | 5 ++ .../gql/fragment-masking.ts | 15 +++- .../gql/fragment-masking.ts | 15 +++- .../gql/fragment-masking.ts | 15 +++- .../graphql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../apollo-client/src/gql/fragment-masking.ts | 15 +++- .../http-executor/src/gql/fragment-masking.ts | 15 +++- .../react/nextjs-swr/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../react/urql/src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- .../vite-react-ts/src/gql/fragment-masking.ts | 15 +++- .../src/gql/fragment-masking.ts | 15 +++- examples/vue/urql/src/gql/fragment-masking.ts | 15 +++- .../vue/villus/src/gql/fragment-masking.ts | 15 +++- .../yoga-tests/src/gql/fragment-masking.ts | 15 +++- .../client/src/fragment-masking-plugin.ts | 78 ++++++++-------- .../client/tests/client-preset.spec.ts | 89 +++++++++++-------- 25 files changed, 382 insertions(+), 120 deletions(-) create mode 100644 .changeset/proud-cycles-design.md diff --git a/.changeset/proud-cycles-design.md b/.changeset/proud-cycles-design.md new file mode 100644 index 00000000000..bf6435d9e3a --- /dev/null +++ b/.changeset/proud-cycles-design.md @@ -0,0 +1,5 @@ +--- +'@graphql-codegen/client-preset': minor +--- + +Preserving `Array` or `ReadonlyArray` in `useFragment()` return type. diff --git a/dev-test/gql-tag-operations-masking/gql/fragment-masking.ts b/dev-test/gql-tag-operations-masking/gql/fragment-masking.ts index a6fea3ebd2c..d2e5a7e8cd4 100644 --- a/dev-test/gql-tag-operations-masking/gql/fragment-masking.ts +++ b/dev-test/gql-tag-operations-masking/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/dev-test/gql-tag-operations-urql/gql/fragment-masking.ts b/dev-test/gql-tag-operations-urql/gql/fragment-masking.ts index a6fea3ebd2c..d2e5a7e8cd4 100644 --- a/dev-test/gql-tag-operations-urql/gql/fragment-masking.ts +++ b/dev-test/gql-tag-operations-urql/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/dev-test/gql-tag-operations/gql/fragment-masking.ts b/dev-test/gql-tag-operations/gql/fragment-masking.ts index a6fea3ebd2c..d2e5a7e8cd4 100644 --- a/dev-test/gql-tag-operations/gql/fragment-masking.ts +++ b/dev-test/gql-tag-operations/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/dev-test/gql-tag-operations/graphql/fragment-masking.ts b/dev-test/gql-tag-operations/graphql/fragment-masking.ts index a6fea3ebd2c..d2e5a7e8cd4 100644 --- a/dev-test/gql-tag-operations/graphql/fragment-masking.ts +++ b/dev-test/gql-tag-operations/graphql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/persisted-documents-string-mode/src/gql/fragment-masking.ts b/examples/persisted-documents-string-mode/src/gql/fragment-masking.ts index c22b351be1a..3347bc92eeb 100644 --- a/examples/persisted-documents-string-mode/src/gql/fragment-masking.ts +++ b/examples/persisted-documents-string-mode/src/gql/fragment-masking.ts @@ -22,11 +22,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -35,10 +45,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/persisted-documents/src/gql/fragment-masking.ts b/examples/persisted-documents/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/persisted-documents/src/gql/fragment-masking.ts +++ b/examples/persisted-documents/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/apollo-client-defer/src/gql/fragment-masking.ts b/examples/react/apollo-client-defer/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/react/apollo-client-defer/src/gql/fragment-masking.ts +++ b/examples/react/apollo-client-defer/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/apollo-client-swc-plugin/src/gql/fragment-masking.ts b/examples/react/apollo-client-swc-plugin/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/react/apollo-client-swc-plugin/src/gql/fragment-masking.ts +++ b/examples/react/apollo-client-swc-plugin/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/apollo-client/src/gql/fragment-masking.ts b/examples/react/apollo-client/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/react/apollo-client/src/gql/fragment-masking.ts +++ b/examples/react/apollo-client/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/http-executor/src/gql/fragment-masking.ts b/examples/react/http-executor/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/react/http-executor/src/gql/fragment-masking.ts +++ b/examples/react/http-executor/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/nextjs-swr/gql/fragment-masking.ts b/examples/react/nextjs-swr/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/react/nextjs-swr/gql/fragment-masking.ts +++ b/examples/react/nextjs-swr/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/tanstack-react-query/src/gql/fragment-masking.ts b/examples/react/tanstack-react-query/src/gql/fragment-masking.ts index c22b351be1a..3347bc92eeb 100644 --- a/examples/react/tanstack-react-query/src/gql/fragment-masking.ts +++ b/examples/react/tanstack-react-query/src/gql/fragment-masking.ts @@ -22,11 +22,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -35,10 +45,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/react/urql/src/gql/fragment-masking.ts b/examples/react/urql/src/gql/fragment-masking.ts index c22b351be1a..3347bc92eeb 100644 --- a/examples/react/urql/src/gql/fragment-masking.ts +++ b/examples/react/urql/src/gql/fragment-masking.ts @@ -22,11 +22,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -35,10 +45,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/typescript-esm/src/gql/fragment-masking.ts b/examples/typescript-esm/src/gql/fragment-masking.ts index a6fea3ebd2c..d2e5a7e8cd4 100644 --- a/examples/typescript-esm/src/gql/fragment-masking.ts +++ b/examples/typescript-esm/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/typescript-graphql-request/src/gql/fragment-masking.ts b/examples/typescript-graphql-request/src/gql/fragment-masking.ts index c22b351be1a..3347bc92eeb 100644 --- a/examples/typescript-graphql-request/src/gql/fragment-masking.ts +++ b/examples/typescript-graphql-request/src/gql/fragment-masking.ts @@ -22,11 +22,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -35,10 +45,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/vite/vite-react-cts/src/gql/fragment-masking.ts b/examples/vite/vite-react-cts/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/vite/vite-react-cts/src/gql/fragment-masking.ts +++ b/examples/vite/vite-react-cts/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/vite/vite-react-mts/src/gql/fragment-masking.ts b/examples/vite/vite-react-mts/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/vite/vite-react-mts/src/gql/fragment-masking.ts +++ b/examples/vite/vite-react-mts/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/vite/vite-react-ts/src/gql/fragment-masking.ts b/examples/vite/vite-react-ts/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/vite/vite-react-ts/src/gql/fragment-masking.ts +++ b/examples/vite/vite-react-ts/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/vue/apollo-composable/src/gql/fragment-masking.ts b/examples/vue/apollo-composable/src/gql/fragment-masking.ts index d88f47b6f04..1fbb84c8ae5 100644 --- a/examples/vue/apollo-composable/src/gql/fragment-masking.ts +++ b/examples/vue/apollo-composable/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/vue/urql/src/gql/fragment-masking.ts b/examples/vue/urql/src/gql/fragment-masking.ts index d88f47b6f04..1fbb84c8ae5 100644 --- a/examples/vue/urql/src/gql/fragment-masking.ts +++ b/examples/vue/urql/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/vue/villus/src/gql/fragment-masking.ts b/examples/vue/villus/src/gql/fragment-masking.ts index d88f47b6f04..1fbb84c8ae5 100644 --- a/examples/vue/villus/src/gql/fragment-masking.ts +++ b/examples/vue/villus/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/examples/yoga-tests/src/gql/fragment-masking.ts b/examples/yoga-tests/src/gql/fragment-masking.ts index c00c8820b30..8dfdc1b8201 100644 --- a/examples/yoga-tests/src/gql/fragment-masking.ts +++ b/examples/yoga-tests/src/gql/fragment-masking.ts @@ -23,11 +23,21 @@ export function useFragment( fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array; +// return array of nullable if `fragmentType` is array of nullable +export function useFragment( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined; +// return readonly array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable +// return readonly array of nullable if `fragmentType` is array of nullable export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined @@ -36,10 +46,11 @@ export function useFragment( _documentNode: DocumentTypeDecoration, fragmentType: | FragmentType> + | Array>> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } diff --git a/packages/presets/client/src/fragment-masking-plugin.ts b/packages/presets/client/src/fragment-masking-plugin.ts index fa374b18132..1d35a7f5c7b 100644 --- a/packages/presets/client/src/fragment-masking-plugin.ts +++ b/packages/presets/client/src/fragment-masking-plugin.ts @@ -22,52 +22,50 @@ export function makeFragmentData< const defaultUnmaskFunctionName = 'useFragment'; -const modifyType = ( - rawType: string, - opts: { nullable: boolean; list: 'with-list' | 'only-list' | false; empty?: boolean } -) => { - return `${ - opts.list === 'only-list' - ? `ReadonlyArray<${rawType}>` - : opts.list === 'with-list' - ? `${rawType} | ReadonlyArray<${rawType}>` - : rawType - }${opts.nullable ? ' | null | undefined' : ''}`; -}; +const createUnmaskFunctionTypeDefinitions = (unmaskFunctionName = defaultUnmaskFunctionName) => [ + `// return non-nullable if \`fragmentType\` is non-nullable +export function ${unmaskFunctionName}( + _documentNode: DocumentTypeDecoration, + fragmentType: FragmentType> +): TType;`, -const createUnmaskFunctionTypeDefinition = ( - unmaskFunctionName = defaultUnmaskFunctionName, - opts: { nullable: boolean; list: 'with-list' | 'only-list' | false } -) => { - return `export function ${unmaskFunctionName}( + `// return nullable if \`fragmentType\` is nullable +export function ${unmaskFunctionName}( _documentNode: DocumentTypeDecoration, - fragmentType: ${modifyType(`FragmentType>`, opts)} -): ${modifyType('TType', opts)}`; -}; + fragmentType: FragmentType> | null | undefined +): TType | null | undefined;`, -const createUnmaskFunctionTypeDefinitions = (unmaskFunctionName = defaultUnmaskFunctionName) => [ - `// return non-nullable if \`fragmentType\` is non-nullable\n${createUnmaskFunctionTypeDefinition( - unmaskFunctionName, - { nullable: false, list: false } - )}`, - `// return nullable if \`fragmentType\` is nullable\n${createUnmaskFunctionTypeDefinition(unmaskFunctionName, { - nullable: true, - list: false, - })}`, - `// return array of non-nullable if \`fragmentType\` is array of non-nullable\n${createUnmaskFunctionTypeDefinition( - unmaskFunctionName, - { nullable: false, list: 'only-list' } - )}`, - `// return array of nullable if \`fragmentType\` is array of nullable\n${createUnmaskFunctionTypeDefinition( - unmaskFunctionName, - { nullable: true, list: 'only-list' } - )}`, + `// return array of non-nullable if \`fragmentType\` is array of non-nullable +export function ${unmaskFunctionName}( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> +): Array;`, + + `// return array of nullable if \`fragmentType\` is array of nullable +export function ${unmaskFunctionName}( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined +): Array | null | undefined;`, + + `// return readonly array of non-nullable if \`fragmentType\` is array of non-nullable +export function ${unmaskFunctionName}( + _documentNode: DocumentTypeDecoration, + fragmentType: ReadonlyArray>> +): ReadonlyArray;`, + + `// return readonly array of nullable if \`fragmentType\` is array of nullable +export function ${unmaskFunctionName}( + _documentNode: DocumentTypeDecoration, + fragmentType: ReadonlyArray>> | null | undefined +): ReadonlyArray | null | undefined;`, ]; const createUnmaskFunction = (unmaskFunctionName = defaultUnmaskFunctionName) => ` -${createUnmaskFunctionTypeDefinitions(unmaskFunctionName) - .concat(createUnmaskFunctionTypeDefinition(unmaskFunctionName, { nullable: true, list: 'with-list' })) - .join(';\n')} { +${createUnmaskFunctionTypeDefinitions(unmaskFunctionName).join('\n')} +export function ${unmaskFunctionName}( + _documentNode: DocumentTypeDecoration, + fragmentType: FragmentType> | Array>> | ReadonlyArray>> | null | undefined +): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } `; diff --git a/packages/presets/client/tests/client-preset.spec.ts b/packages/presets/client/tests/client-preset.spec.ts index 67d7874667c..72123550326 100644 --- a/packages/presets/client/tests/client-preset.spec.ts +++ b/packages/presets/client/tests/client-preset.spec.ts @@ -782,19 +782,29 @@ export * from "./gql";`); fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if \`fragmentType\` is array of non-nullable + export function iLikeTurtles( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> + ): Array; + // return array of nullable if \`fragmentType\` is array of nullable + export function iLikeTurtles( + _documentNode: DocumentTypeDecoration, + fragmentType: Array>> | null | undefined + ): Array | null | undefined; + // return readonly array of non-nullable if \`fragmentType\` is array of non-nullable export function iLikeTurtles( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> ): ReadonlyArray; - // return array of nullable if \`fragmentType\` is array of nullable + // return readonly array of nullable if \`fragmentType\` is array of nullable export function iLikeTurtles( _documentNode: DocumentTypeDecoration, fragmentType: ReadonlyArray>> | null | undefined ): ReadonlyArray | null | undefined; export function iLikeTurtles( _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> | ReadonlyArray>> | null | undefined - ): TType | ReadonlyArray | null | undefined { + fragmentType: FragmentType> | Array>> | ReadonlyArray>> | null | undefined + ): TType | Array | ReadonlyArray | null | undefined { return fragmentType as any; } @@ -823,39 +833,6 @@ export * from "./gql";`); } " `); - - expect(gqlFile.content).toBeSimilarStringTo(` - export function iLikeTurtles( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> - ): TType; - `); - expect(gqlFile.content).toBeSimilarStringTo(` - export function iLikeTurtles( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> | null | undefined - ): TType | null | undefined; - `); - expect(gqlFile.content).toBeSimilarStringTo(` - export function iLikeTurtles( - _documentNode: DocumentTypeDecoration, - fragmentType: ReadonlyArray>> - ): ReadonlyArray; - `); - expect(gqlFile.content).toBeSimilarStringTo(` - export function iLikeTurtles( - _documentNode: DocumentTypeDecoration, - fragmentType: ReadonlyArray>> | null | undefined - ): ReadonlyArray | null | undefined; - `); - expect(gqlFile.content).toBeSimilarStringTo(` - export function iLikeTurtles( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> | ReadonlyArray>> | null | undefined - ): TType | ReadonlyArray | null | undefined { - return fragmentType as any; - } - `); }); it('can accept null in useFragment', async () => { @@ -923,6 +900,46 @@ export * from "./gql";`); }, }); + const content = mergeOutputs([ + ...result, + fs.readFileSync(docPath, 'utf8'), + ` + function App(props: { foos: Array> }) { + const fragments: Array = useFragment(Fragment, props.foos); + return fragments.map(f => f.value); + } + `, + ]); + + validateTs(content, undefined, false, true, [`Duplicate identifier 'DocumentNode'.`], true); + }); + + it('useFragment preserves ReadonlyArray type', async () => { + const docPath = path.join(__dirname, 'fixtures/with-fragment.ts'); + const result = await executeCodegen({ + schema: [ + /* GraphQL */ ` + type Query { + foo: Foo + foos: [Foo!] + } + + type Foo { + value: String + } + `, + ], + documents: docPath, + generates: { + 'out1/': { + preset, + presetConfig: { + fragmentMasking: true, + }, + }, + }, + }); + const content = mergeOutputs([ ...result, fs.readFileSync(docPath, 'utf8'),