diff --git a/codegen.ts b/codegen.ts index 94f4c89..8d4b636 100644 --- a/codegen.ts +++ b/codegen.ts @@ -2,11 +2,12 @@ import { CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { schema: "https://swapi-graphql.netlify.app/.netlify/functions/index", - documents: ["src/**/*.tsx"], - ignoreNoDocuments: true, // for better experience with the watcher + documents: ["src/**/*.gql"], + // ignoreNoDocuments: true, // for better experience with the watcher generates: { - "./src/gql/": { - preset: "client", + "./src/gql/generated.ts": { + plugins: ["typescript", "typescript-operations", "typed-document-node"], + // preset: "client", }, }, }; diff --git a/package-lock.json b/package-lock.json index b072203..0db6b2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,9 @@ "devDependencies": { "@graphql-codegen/cli": "^5.0.0", "@graphql-codegen/client-preset": "^4.1.0", + "@graphql-codegen/typed-document-node": "^5.0.1", + "@graphql-codegen/typescript": "^4.0.1", + "@graphql-codegen/typescript-operations": "^4.0.1", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@typescript-eslint/eslint-plugin": "^6.0.0", diff --git a/package.json b/package.json index 019ded0..587dd0b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "devDependencies": { "@graphql-codegen/cli": "^5.0.0", "@graphql-codegen/client-preset": "^4.1.0", + "@graphql-codegen/typed-document-node": "^5.0.1", + "@graphql-codegen/typescript": "^4.0.1", + "@graphql-codegen/typescript-operations": "^4.0.1", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@typescript-eslint/eslint-plugin": "^6.0.0", diff --git a/src/App.tsx b/src/App.tsx index 35c5bcb..3711c61 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,17 @@ -import React from "react"; import { useQuery } from "@apollo/client"; import "./App.css"; import Film from "./Film"; -import { graphql } from "../src/gql"; - -const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ ` - query allFilmsWithVariablesQuery($first: Int!) { - allFilms(first: $first) { - edges { - node { - ...FilmItem - } - } - } - } -`); +import { + AllFilmsWithVariablesQueryDocument, + AllFilmsWithVariablesQueryQueryVariables, +} from "./gql/generated"; function App() { - // `data` is typed! - const { data } = useQuery(allFilmsWithVariablesQueryDocument, { - variables: { first: 10 }, + // `data` and `variables` are typed! + const variables: AllFilmsWithVariablesQueryQueryVariables = { first: 10 }; + const { data } = useQuery(AllFilmsWithVariablesQueryDocument, { + variables: variables, }); return (
diff --git a/src/Film.tsx b/src/Film.tsx index 6769409..2219a23 100644 --- a/src/Film.tsx +++ b/src/Film.tsx @@ -1,24 +1,13 @@ -import { FragmentType, useFragment } from "./gql/fragment-masking"; -import { graphql } from "../src/gql"; - -export const FilmFragment = graphql(/* GraphQL */ ` - fragment FilmItem on Film { - id - title - releaseDate - producers - } -`); +import { Film as GqlFilm } from "./gql/generated"; const Film = (props: { /* `film` property has the correct type 🎉 */ - film: FragmentType; + film: GqlFilm; }) => { - const film = useFragment(FilmFragment, props.film); return (
-

{film.title}

-

{film.releaseDate}

+

{props.film.title}

+

{props.film.releaseDate}

); }; diff --git a/src/gql/allFilmsWithVariablesQuery.gql b/src/gql/allFilmsWithVariablesQuery.gql new file mode 100644 index 0000000..304c65c --- /dev/null +++ b/src/gql/allFilmsWithVariablesQuery.gql @@ -0,0 +1,9 @@ +query allFilmsWithVariablesQuery($first: Int!) { + allFilms(first: $first) { + edges { + node { + ...FilmItem + } + } + } +} diff --git a/src/gql/filmFragment.gql b/src/gql/filmFragment.gql new file mode 100644 index 0000000..53de671 --- /dev/null +++ b/src/gql/filmFragment.gql @@ -0,0 +1,6 @@ +fragment FilmItem on Film { + id + title + releaseDate + producers +} diff --git a/src/gql/fragment-masking.ts b/src/gql/fragment-masking.ts deleted file mode 100644 index 2ba06f1..0000000 --- a/src/gql/fragment-masking.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core'; -import { FragmentDefinitionNode } from 'graphql'; -import { Incremental } from './graphql'; - - -export type FragmentType> = TDocumentType extends DocumentTypeDecoration< - infer TType, - any -> - ? [TType] extends [{ ' $fragmentName'?: infer TKey }] - ? TKey extends string - ? { ' $fragmentRefs'?: { [key in TKey]: TType } } - : never - : never - : never; - -// return non-nullable if `fragmentType` is non-nullable -export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> -): TType; -// return nullable if `fragmentType` is nullable -export function useFragment( - _documentNode: DocumentTypeDecoration, - 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: ReadonlyArray>> -): ReadonlyArray; -// return array of nullable if `fragmentType` is array of nullable -export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: ReadonlyArray>> | null | undefined -): ReadonlyArray | null | undefined; -export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> | ReadonlyArray>> | null | undefined -): TType | ReadonlyArray | null | undefined { - return fragmentType as any; -} - - -export function makeFragmentData< - F extends DocumentTypeDecoration, - FT extends ResultOf ->(data: FT, _fragment: F): FragmentType { - return data as FragmentType; -} -export function isFragmentReady( - queryNode: DocumentTypeDecoration, - fragmentNode: TypedDocumentNode, - data: FragmentType, any>> | null | undefined -): data is FragmentType { - const deferredFields = (queryNode as { __meta__?: { deferredFields: Record } }).__meta__ - ?.deferredFields; - - if (!deferredFields) return true; - - const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined; - const fragName = fragDef?.name?.value; - - const fields = (fragName && deferredFields[fragName]) || []; - return fields.length > 0 && fields.every(field => data && field in data); -} diff --git a/src/gql/graphql.ts b/src/gql/generated.ts similarity index 99% rename from src/gql/graphql.ts rename to src/gql/generated.ts index d71aa4f..c141aca 100644 --- a/src/gql/graphql.ts +++ b/src/gql/generated.ts @@ -1,4 +1,3 @@ -/* eslint-disable */ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; export type Maybe = T | null; export type InputMaybe = Maybe; @@ -1309,12 +1308,9 @@ export type AllFilmsWithVariablesQueryQueryVariables = Exact<{ }>; -export type AllFilmsWithVariablesQueryQuery = { __typename?: 'Root', allFilms?: { __typename?: 'FilmsConnection', edges?: Array<{ __typename?: 'FilmsEdge', node?: ( - { __typename?: 'Film' } - & { ' $fragmentRefs'?: { 'FilmItemFragment': FilmItemFragment } } - ) | null } | null> | null } | null }; +export type AllFilmsWithVariablesQueryQuery = { __typename?: 'Root', allFilms?: { __typename?: 'FilmsConnection', edges?: Array<{ __typename?: 'FilmsEdge', node?: { __typename?: 'Film', id: string, title?: string | null, releaseDate?: string | null, producers?: Array | null } | null } | null> | null } | null }; -export type FilmItemFragment = { __typename?: 'Film', id: string, title?: string | null, releaseDate?: string | null, producers?: Array | null } & { ' $fragmentName'?: 'FilmItemFragment' }; +export type FilmItemFragment = { __typename?: 'Film', id: string, title?: string | null, releaseDate?: string | null, producers?: Array | null }; 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; 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"}}]}}]}}]}}]}},{"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; \ No newline at end of file diff --git a/src/gql/gql.ts b/src/gql/gql.ts deleted file mode 100644 index 897dbaa..0000000 --- a/src/gql/gql.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* eslint-disable */ -import * as types from './graphql'; -import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; - -/** - * Map of all GraphQL operations in the project. - * - * This map has several performance disadvantages: - * 1. It is not tree-shakeable, so it will include all operations in the project. - * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. - * 3. It does not support dead code elimination, so it will add unused operations. - * - * Therefore it is highly recommended to use the babel or swc 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, -}; - -/** - * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - * - * - * @example - * ```ts - * const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`); - * ``` - * - * The query argument is unknown! - * Please regenerate the types. - */ -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"]; -/** - * 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: string) { - return (documents as any)[source] ?? {}; -} - -export type DocumentType> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; \ No newline at end of file diff --git a/src/gql/index.ts b/src/gql/index.ts deleted file mode 100644 index f515991..0000000 --- a/src/gql/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./fragment-masking"; -export * from "./gql"; \ No newline at end of file