Skip to content

Commit

Permalink
feat(codegen): change to use .graphql file
Browse files Browse the repository at this point in the history
Considering migration from a project already using codegen,
I would like to handle typed-document-node from a `.graphql` extension file.

It is possible to generate TypedDocumentNode from a `.graphql` extension file,
and although it is possible to import them at the point of use,
importing from /src/gql/graphql again leads to an increase in file chunks after bundling.

Therefore, it attempts to split the file using the near-operation-file preset.
Even for TypedDocumentNode imported from a split file, type conversion by fragment-masking was possible.
  • Loading branch information
MH4GF committed Jan 21, 2023
1 parent af2bb5e commit e45ef9a
Show file tree
Hide file tree
Showing 14 changed files with 72 additions and 67 deletions.
7 changes: 5 additions & 2 deletions codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
schema: "https://swapi-graphql.netlify.app/.netlify/functions/index",
documents: ["src/**/*.tsx", "!src/gql/**/*"],
documents: ["src/**/*.graphql"],
generates: {
"./src/gql/": {
preset: "client",
Expand All @@ -14,7 +14,10 @@ const config: CodegenConfig = {
extension: ".generated.ts",
baseTypesPath: "~~/gql/graphql",
},
plugins: ["typescript-msw"], // オペレーション定義からMSWのハンドラを生成するプラグイン
plugins: [
"typescript-msw", // オペレーション定義からMSWのハンドラを生成するプラグイン
"typed-document-node",
],
config: {
typesPrefix: "Types.",
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@graphql-codegen/client-preset": "^1.2.6",
"@graphql-codegen/import-types-preset": "^2.2.6",
"@graphql-codegen/near-operation-file-preset": "^2.5.0",
"@graphql-codegen/typed-document-node": "^2.3.12",
"@graphql-codegen/typescript-msw": "^1.1.6",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 0 additions & 20 deletions src/App.generated.ts

This file was deleted.

22 changes: 3 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
import { useQuery } from "@apollo/client";
import { graphql } from "./gql/gql"; // 生成されたコードから `graphql()` をimport
import { AllFilmsQueryDocument } from "./allFilmsQuery.generated";
import Film from "./Film";

// GraphQLで取得したい内容を定義
// ここで定義した内容はTypedDocumentNode(後述)として型付けされる
const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ `
query allFilmsWithVariablesQuery($first: Int!) {
allFilms(first: $first) {
edges {
node {
...FilmItem
}
}
}
}
`);

function App() {
// ほとんどのGraphQLクライアントはTypedDocumentNodeを扱う方法を知っている
// ドキュメントをuseQueryに渡すだけで、返却されるdataや第二引数で渡すvariablesも型付けがされている!
const { data } = useQuery(allFilmsWithVariablesQueryDocument, {
variables: { first: 10 },
const { data } = useQuery(AllFilmsQueryDocument, {
variables: { first: 10, last: 10 },
});
return (
<div className="App">
Expand Down
1 change: 0 additions & 1 deletion src/Film.generated.ts

This file was deleted.

15 changes: 3 additions & 12 deletions src/Film.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import { FilmItemFragmentDoc } from "./FilmItem.generated";
import { FragmentType, useFragment } from "./gql/fragment-masking";
import { graphql } from "./gql/gql";

export const FilmFragment = graphql(/* GraphQL */ `
fragment FilmItem on Film {
id
title
releaseDate
producers
}
`);

const Film = (props: { film: FragmentType<typeof FilmFragment> }) => {
const film = useFragment(FilmFragment, props.film);
const Film = (props: { film: FragmentType<typeof FilmItemFragmentDoc> }) => {
const film = useFragment(FilmItemFragmentDoc, props.film);
return (
<div>
<h3>{film.title}</h3>
Expand Down
4 changes: 4 additions & 0 deletions src/FilmItem.generated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as Types from '~/gql/graphql';

import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export const FilmItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FilmItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Film"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"releaseDate"}},{"kind":"Field","name":{"kind":"Name","value":"producers"}}]}}]} as unknown as DocumentNode<Types.FilmItemFragment, unknown>;
6 changes: 6 additions & 0 deletions src/FilmItem.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fragment FilmItem on Film {
id
title
releaseDate
producers
}
25 changes: 25 additions & 0 deletions src/allFilmsQuery.generated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as Types from '~/gql/graphql';

import { graphql, ResponseResolver, GraphQLRequest, GraphQLContext } from 'msw'
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
import { FilmItemFragmentDoc } from './FilmItem.generated';

/**
* @param resolver a function that accepts a captured request and may return a mocked response.
* @see https://mswjs.io/docs/basics/response-resolver
* @example
* mockAllFilmsQueryQuery((req, res, ctx) => {
* const { first, last } = req.variables;
* return res(
* ctx.data({ allFilms })
* )
* })
*/
export const mockAllFilmsQueryQuery = (resolver: ResponseResolver<GraphQLRequest<Types.AllFilmsQueryQueryVariables>, GraphQLContext<Types.AllFilmsQueryQuery>, any>) =>
graphql.query<Types.AllFilmsQueryQuery, Types.AllFilmsQueryQueryVariables>(
'allFilmsQuery',
resolver
)


export const AllFilmsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allFilmsQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"last"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allFilms"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"Variable","name":{"kind":"Name","value":"last"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FilmItem"}}]}}]}}]}}]}},...FilmItemFragmentDoc.definitions]} as unknown as DocumentNode<Types.AllFilmsQueryQuery, Types.AllFilmsQueryQueryVariables>;
9 changes: 9 additions & 0 deletions src/allFilmsQuery.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query allFilmsQuery($first: Int!, $last: Int!) {
allFilms(first: $first, last: $last) {
edges {
node {
...FilmItem
}
}
}
}
8 changes: 4 additions & 4 deletions src/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
* Therefore it is highly recommended to use the babel-plugin for production.
*/
const documents = {
"\n query allFilmsWithVariablesQuery($first: Int!) {\n allFilms(first: $first) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n }\n": types.AllFilmsWithVariablesQueryDocument,
"\n fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n }\n": types.FilmItemFragmentDoc,
"fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n}": types.FilmItemFragmentDoc,
"query allFilmsQuery($first: Int!, $last: Int!) {\n allFilms(first: $first, last: $last) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n}": types.AllFilmsQueryDocument,
};

/**
Expand All @@ -34,11 +34,11 @@ export function graphql(source: string): unknown;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query allFilmsWithVariablesQuery($first: Int!) {\n allFilms(first: $first) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n }\n"): (typeof documents)["\n query allFilmsWithVariablesQuery($first: Int!) {\n allFilms(first: $first) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n }\n"];
export function graphql(source: "fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n}"): (typeof documents)["fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n}"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n }\n"): (typeof documents)["\n fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n }\n"];
export function graphql(source: "query allFilmsQuery($first: Int!, $last: Int!) {\n allFilms(first: $first, last: $last) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n}"): (typeof documents)["query allFilmsQuery($first: Int!, $last: Int!) {\n allFilms(first: $first, last: $last) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n}"];

export function graphql(source: string) {
return (documents as any)[source] ?? {};
Expand Down
11 changes: 6 additions & 5 deletions src/gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1302,17 +1302,18 @@ export type VehiclesEdge = {
node?: Maybe<Vehicle>;
};

export type AllFilmsWithVariablesQueryQueryVariables = Exact<{
export type FilmItemFragment = { __typename?: 'Film', id: string, title?: string | null, releaseDate?: string | null, producers?: Array<string | null> | null } & { ' $fragmentName'?: 'FilmItemFragment' };

export type AllFilmsQueryQueryVariables = Exact<{
first: Scalars['Int'];
last: Scalars['Int'];
}>;


export type AllFilmsWithVariablesQueryQuery = { __typename?: 'Root', allFilms?: { __typename?: 'FilmsConnection', edges?: Array<{ __typename?: 'FilmsEdge', node?: (
export type AllFilmsQueryQuery = { __typename?: 'Root', allFilms?: { __typename?: 'FilmsConnection', edges?: Array<{ __typename?: 'FilmsEdge', node?: (
{ __typename?: 'Film' }
& { ' $fragmentRefs'?: { 'FilmItemFragment': FilmItemFragment } }
) | null } | null> | null } | null };

export type FilmItemFragment = { __typename?: 'Film', id: string, title?: string | null, releaseDate?: string | null, producers?: Array<string | null> | null } & { ' $fragmentName'?: 'FilmItemFragment' };

export const FilmItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FilmItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Film"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"releaseDate"}},{"kind":"Field","name":{"kind":"Name","value":"producers"}}]}}]} as unknown as DocumentNode<FilmItemFragment, unknown>;
export const AllFilmsWithVariablesQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allFilmsWithVariablesQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allFilms"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FilmItem"}}]}}]}}]}}]}},...FilmItemFragmentDoc.definitions]} as unknown as DocumentNode<AllFilmsWithVariablesQueryQuery, AllFilmsWithVariablesQueryQueryVariables>;
export const AllFilmsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"allFilmsQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"last"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allFilms"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"Variable","name":{"kind":"Name","value":"last"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FilmItem"}}]}}]}}]}}]}},...FilmItemFragmentDoc.definitions]} as unknown as DocumentNode<AllFilmsQueryQuery, AllFilmsQueryQueryVariables>;
8 changes: 4 additions & 4 deletions src/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { mockAllFilmsWithVariablesQueryQuery } from "~/App.generated";
import { FilmFragment } from "../Film";
import { mockAllFilmsQueryQuery } from "~/allFilmsQuery.generated";
import { FilmItemFragmentDoc } from "~/FilmItem.generated";
import { makeFragmentData } from "../gql";

export const handlers = [
mockAllFilmsWithVariablesQueryQuery((req, res, ctx) => {
mockAllFilmsQueryQuery((req, res, ctx) => {
return res(
ctx.data({
allFilms: {
Expand All @@ -15,7 +15,7 @@ export const handlers = [
title: "A New Hope",
releaseDate: "1977-05-25",
},
FilmFragment
FilmItemFragmentDoc
),
},
],
Expand Down

0 comments on commit e45ef9a

Please sign in to comment.