graphql-codegen のいろいろなプラグイン・オプション・プリセットを試す
inlineFragmentTypes
typescript-operations plugin の @graphql-codegen/cli
の2.0.0 から入った
fragment から生成した型定義で、他の fragment の型定義を参照するのではなく展開するようになる(inline
)
combine
を指定する(2.0.0 以前の挙動)
--- src/__generated__/000-basic/nested.ts 2022-03-24 15:54:06.000000000 +0900
+++ src/__generated__/001-inlineFragmentTypes-combine/nested.ts 2022-03-24 15:54:06.000000000 +0900
@@ -11,7 +11,7 @@
export type PostHeaderFragment = {
__typename?: "Post";
title: string;
- author: { __typename?: "User"; username: string; avatarUrl?: string | null };
+ author: { __typename?: "User" } & PostHeaderUserFragment;
};
export type GetPostHeaderQueryVariables = Types.Exact<{
@@ -23,12 +23,6 @@
postById: {
__typename?: "Post";
id: string;
- title: string;
- author: {
- __typename?: "User";
- id: string;
- username: string;
- avatarUrl?: string | null;
- };
- };
+ author: { __typename?: "User"; id: string };
+ } & PostHeaderFragment;
};
(https://github.com/izumin5210-sandbox/graphql-codegen-sandbox/tree/e0beacf で確認)
mask
--- src/__generated__/000-basic/nested.ts 2022-03-24 15:54:06.000000000 +0900
+++ src/__generated__/002-inlineFragmentTypes-mask/nested.ts 2022-03-24 15:54:06.000000000 +0900
@@ -6,13 +6,15 @@
__typename?: "User";
username: string;
avatarUrl?: string | null;
-};
+} & { " $fragmentName": "PostHeaderUserFragment" };
export type PostHeaderFragment = {
__typename?: "Post";
title: string;
- author: { __typename?: "User"; username: string; avatarUrl?: string | null };
-};
+ author: { __typename?: "User" } & {
+ " $fragmentRefs": { PostHeaderUserFragment: PostHeaderUserFragment };
+ };
+} & { " $fragmentName": "PostHeaderFragment" };
export type GetPostHeaderQueryVariables = Types.Exact<{
postId: Types.Scalars["String"];
@@ -23,12 +25,6 @@
postById: {
__typename?: "Post";
id: string;
- title: string;
- author: {
- __typename?: "User";
- id: string;
- username: string;
- avatarUrl?: string | null;
- };
- };
+ author: { __typename?: "User"; id: string };
+ } & { " $fragmentRefs": { PostHeaderFragment: PostHeaderFragment } };
};
(https://github.com/izumin5210-sandbox/graphql-codegen-sandbox/tree/e0beacf で確認)
mask
を指定すると、そもそも子 fragment で参照されたフィールドに直接アクセスが不可能となる
dedupeFragments
「データ転送量削減のための fragment 重複排除」
1つのクエリに同一のフラグメント定義が複数存在する場合に、その重複を排除する
いくつかのフラグメントでばらばらに同一フラグメントを参照すると起きる
有効にすると、typed-document-node の出力が変わる
- Fragment に別の Fragment の document を埋め込むのをやめる
- 代わりに、Query や Mutaiton の document に依存 Fragment すべてを埋め込む
まあ大体正しく動くんだけど、Fragment の document が単体で利用できなくなるので困るケースもありそう
具体的には Apollo の readFragment
/ writeFragment
だったり、graphql-anywhere のような fragment masking 実装は正常に動かなくなる気がする
--- src/__generated__/000-basic-with-typed-document-node/nested.ts 2022-03-24 16:18:13.000000000 +0900
+++ src/__generated__/003-typed-document-node-dedupe/nested.ts 2022-03-24 16:18:13.000000000 +0900
@@ -84,7 +84,6 @@
],
},
},
- ...PostHeaderUserFragmentDoc.definitions,
],
} as unknown as DocumentNode<PostHeaderFragment, unknown>;
export const GetPostHeaderDocument = {
@@ -151,5 +150,6 @@
},
},
...PostHeaderFragmentDoc.definitions,
+ ...PostHeaderUserFragmentDoc.definitions,
],
} as unknown as DocumentNode<GetPostHeaderQuery, GetPostHeaderQueryVariables>;
typescript-operations 自体には作用しない… そりゃそうなんだけど、じゃあなんで typescript-operations のおぷしょんになってるんだろう
→ operations だけでなく、(@graphql-codegen/visotor-plugin-common).Visitor
を利用するすべてのプラグインが持つオプションかも
(https://github.com/izumin5210-sandbox/graphql-codegen-sandbox/tree/e0beacf で確認)
具体的には Apollo の
readFragment
/writeFragment
だったり、graphql-anywhere のような fragment masking 実装は正常に動かなくなる気がする
issue あがってた
gql-tag-operations preset
Fragment 定義をそのまま文字列化したものをキーにし、値に Fragment の DocumentNode を持つ object を生成する
この object をもとにして激ヤバオーバーロードして gql
関数を生成することで、gql
に Document 文字列を渡せば DocumentNode が返ってくる関数となる
生成される DocumentNode は TypedDocumentNode になる
@graphql-codegen/typed-document-node
と @graphql-codegen/typescript-operations
を内包しているイメージか
fragmentMasking
gql-tag-operations preset の useFragment
関数が生成される
// https://github.com/izumin5210-sandbox/graphql-codegen-sandbox/blob/f28b49b/src/__generated__/005-gql-tag-operations-preset-fragment-masking/fragment-masking.ts
export type FragmentType<TDocumentType extends DocumentNode<any, any>> =
TDocumentType extends DocumentNode<infer TType, any>
? TType extends { " $fragmentName": infer TKey }
? TKey extends string
? { " $fragmentRefs": { [key in TKey]: TType } }
: never
: never
: never;
export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>>
): TType {
return fragmentType as any;
}
型定義にも diff が発生する
typescript-operations plugin で inlineFragmentTypes: mask
にしたのと同じ状態かな?
`fragmentMasking` の有無による生成物の違い
Only in src/__generated__/005-gql-tag-operations-preset-fragment-masking: fragment-masking.ts
diff -u src/__generated__/004-gql-tag-operations-preset/graphql.ts src/__generated__/005-gql-tag-operations-preset-fragment-masking/graphql.ts
--- src/__generated__/004-gql-tag-operations-preset/graphql.ts 2022-03-24 16:44:18.000000000 +0900
+++ src/__generated__/005-gql-tag-operations-preset-fragment-masking/graphql.ts 2022-03-24 16:44:18.000000000 +0900
@@ -85,7 +85,7 @@
title: string;
body: string;
user: { __typename?: "User"; username: string; thumbnailUrl?: string | null };
-};
+} & { " $fragmentName": "PostSummaryFragment" };
export type GetPostSummaryQueryVariables = Exact<{
postId: Scalars["String"];
@@ -93,16 +93,8 @@
export type GetPostSummaryQuery = {
__typename?: "Query";
- postById: {
- __typename?: "Post";
- id: string;
- title: string;
- body: string;
- user: {
- __typename?: "User";
- username: string;
- thumbnailUrl?: string | null;
- };
+ postById: { __typename?: "Post"; id: string } & {
+ " $fragmentRefs": { PostSummaryFragment: PostSummaryFragment };
};
};
@@ -110,7 +102,7 @@
__typename?: "Post";
title: string;
author: { __typename?: "User"; username: string };
-};
+} & { " $fragmentName": "PostWithAuthorFragment" };
export type GetPostWithAuthorQueryVariables = Exact<{
postId: Scalars["String"];
@@ -118,31 +110,31 @@
export type GetPostWithAuthorQuery = {
__typename?: "Query";
- postById: {
- __typename?: "Post";
- id: string;
- title: string;
- author: { __typename?: "User"; username: string };
+ postById: { __typename?: "Post"; id: string } & {
+ " $fragmentRefs": { PostWithAuthorFragment: PostWithAuthorFragment };
};
};
export type PostUserAvatarFragment = {
__typename?: "Post";
author: { __typename?: "User"; avatarUrl?: string | null };
-};
+} & { " $fragmentName": "PostUserAvatarFragment" };
-export type PostDetailHeaderFragment = {
+export type PostDetailHeaderFragment = ({
__typename?: "Post";
title: string;
- author: { __typename?: "User"; avatarUrl?: string | null };
-};
+} & {
+ " $fragmentRefs": { PostUserAvatarFragment: PostUserAvatarFragment };
+}) & { " $fragmentName": "PostDetailHeaderFragment" };
-export type PostDetailFragment = {
+export type PostDetailFragment = ({
__typename?: "Post";
title: string;
body: string;
- author: { __typename?: "User"; username: string; avatarUrl?: string | null };
-};
+ author: { __typename?: "User"; username: string };
+} & {
+ " $fragmentRefs": { PostUserAvatarFragment: PostUserAvatarFragment };
+}) & { " $fragmentName": "PostDetailFragment" };
export type GetPostDetailQueryVariables = Exact<{
postId: Scalars["String"];
@@ -150,15 +142,10 @@
export type GetPostDetailQuery = {
__typename?: "Query";
- postById: {
- __typename?: "Post";
- id: string;
- title: string;
- body: string;
- author: {
- __typename?: "User";
- username: string;
- avatarUrl?: string | null;
+ postById: { __typename?: "Post"; id: string } & {
+ " $fragmentRefs": {
+ PostDetailFragment: PostDetailFragment;
+ PostDetailHeaderFragment: PostDetailHeaderFragment;
};
};
};
@@ -167,13 +154,15 @@
__typename?: "User";
username: string;
avatarUrl?: string | null;
-};
+} & { " $fragmentName": "PostHeaderUserFragment" };
export type PostHeaderFragment = {
__typename?: "Post";
title: string;
- author: { __typename?: "User"; username: string; avatarUrl?: string | null };
-};
+ author: { __typename?: "User" } & {
+ " $fragmentRefs": { PostHeaderUserFragment: PostHeaderUserFragment };
+ };
+} & { " $fragmentName": "PostHeaderFragment" };
export type GetPostHeaderQueryVariables = Exact<{
postId: Scalars["String"];
@@ -184,14 +173,8 @@
postById: {
__typename?: "Post";
id: string;
- title: string;
- author: {
- __typename?: "User";
- id: string;
- username: string;
- avatarUrl?: string | null;
- };
- };
+ author: { __typename?: "User"; id: string };
+ } & { " $fragmentRefs": { PostHeaderFragment: PostHeaderFragment } };
};
export type PostWithCommentsFragment = {
@@ -199,7 +182,7 @@
title: string;
body: string;
comments: Array<{ __typename?: "Comment"; body: string }>;
-};
+} & { " $fragmentName": "PostWithCommentsFragment" };
export type GetPostWithCommentsQueryVariables = Exact<{
postId: Scalars["String"];
@@ -210,9 +193,9 @@
postById: {
__typename?: "Post";
id: string;
- title: string;
- body: string;
- comments: Array<{ __typename?: "Comment"; id: string; body: string }>;
+ comments: Array<{ __typename?: "Comment"; id: string }>;
+ } & {
+ " $fragmentRefs": { PostWithCommentsFragment: PostWithCommentsFragment };
};
};
@@ -225,7 +208,7 @@
avatarUrl?: string | null;
username: string;
};
-};
+} & { " $fragmentName": "PostListItemFragment" };
export type ListPostsQueryVariables = Exact<{
userId: Scalars["String"];
@@ -233,24 +216,18 @@
export type ListPostsQuery = {
__typename?: "Query";
- postsByUserId: Array<{
- __typename?: "Post";
- id: string;
- title: string;
- author: {
- __typename?: "User";
- id: string;
- avatarUrl?: string | null;
- username: string;
- };
- }>;
+ postsByUserId: Array<
+ { __typename?: "Post"; id: string } & {
+ " $fragmentRefs": { PostListItemFragment: PostListItemFragment };
+ }
+ >;
};
export type UserHeaderFragment = {
__typename?: "User";
username: string;
avatarUrl?: string | null;
-};
+} & { " $fragmentName": "UserHeaderFragment" };
export type GetUserHeaderQueryVariables = Exact<{
userId: Scalars["String"];
@@ -258,26 +235,31 @@
export type GetUserHeaderQuery = {
__typename?: "Query";
- userById: {
- __typename?: "User";
- id: string;
- username: string;
- avatarUrl?: string | null;
+ userById: { __typename?: "User"; id: string } & {
+ " $fragmentRefs": { UserHeaderFragment: UserHeaderFragment };
};
};
-export type PostImageFragment = { __typename?: "Image"; imageUrl: string };
+export type PostImageFragment = { __typename?: "Image"; imageUrl: string } & {
+ " $fragmentName": "PostImageFragment";
+};
-export type PostVideoFragment = { __typename?: "Video"; videoUrl: string };
+export type PostVideoFragment = { __typename?: "Video"; videoUrl: string } & {
+ " $fragmentName": "PostVideoFragment";
+};
export type PostWithAttachmentsFragment = {
__typename?: "Post";
title: string;
attachmentFiles: Array<
- | { __typename?: "Image"; imageUrl: string }
- | { __typename?: "Video"; videoUrl: string }
+ | ({ __typename?: "Image" } & {
+ " $fragmentRefs": { PostImageFragment: PostImageFragment };
+ })
+ | ({ __typename?: "Video" } & {
+ " $fragmentRefs": { PostVideoFragment: PostVideoFragment };
+ })
>;
-};
+} & { " $fragmentName": "PostWithAttachmentsFragment" };
export type GetPostWithAttachmentsQueryVariables = Exact<{
postId: Scalars["String"];
@@ -285,13 +267,10 @@
export type GetPostWithAttachmentsQuery = {
__typename?: "Query";
- postById: {
- __typename?: "Post";
- title: string;
- attachmentFiles: Array<
- | { __typename?: "Image"; imageUrl: string }
- | { __typename?: "Video"; videoUrl: string }
- >;
+ postById: { __typename?: "Post" } & {
+ " $fragmentRefs": {
+ PostWithAttachmentsFragment: PostWithAttachmentsFragment;
+ };
};
};
diff -u src/__generated__/004-gql-tag-operations-preset/index.ts src/__generated__/005-gql-tag-operations-preset-fragment-masking/index.ts
--- src/__generated__/004-gql-tag-operations-preset/index.ts 2022-03-24 16:44:18.000000000 +0900
+++ src/__generated__/005-gql-tag-operations-preset-fragment-masking/index.ts 2022-03-24 16:44:18.000000000 +0900
@@ -1 +1,2 @@
export * from "./gql";
+export * from "./fragment-masking";
useFragment
自体は何もしない(型変換してるだけ)
gql-tag-operations の生成コードにも依存してないので、typescript-operations
と組み合わせても使えそう