Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TypedDocumentNode string alternative #9137

Merged
merged 35 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4a7b339
Add unit tests
beerose Mar 8, 2023
504367a
Add TypedDocumentNode string alternative
beerose Mar 8, 2023
c7105db
Temporarily use snapshot version of @graphql-typed-document-node/core
beerose Mar 9, 2023
13e48a9
chore(dependencies): updated changesets for modified dependencies
github-actions[bot] Mar 9, 2023
02ed2b3
Update fragment-masking.ts files
beerose Mar 9, 2023
2d5a4e7
Update fragment-masking in dev-test dir
beerose Mar 9, 2023
590b2e2
Update @graphql-typed-document-node/core version in examples
beerose Mar 9, 2023
8e4e93f
Merge branch 'master' into typed-document-string
beerose Mar 9, 2023
7a5d42d
Update test after latest implememntation changes
beerose Mar 9, 2023
3892ba2
Changeset
beerose Mar 10, 2023
ef28588
Modify existing example
beerose Mar 10, 2023
961251f
Update typescript-graphql-request example
beerose Mar 13, 2023
dbd2053
Merge branch 'master' into typed-document-string
beerose Mar 13, 2023
8005ea3
Add failing test for duplicated fragments
beerose Mar 13, 2023
2c6ee68
Inline fragments in string document mode
beerose Mar 13, 2023
7b073f3
Update example with fragment inlining
beerose Mar 13, 2023
db98f71
Support __meta__ with typed document string
beerose Mar 13, 2023
346e1e5
Add TypedDocumentString class to handle metadata
beerose Mar 14, 2023
4e732da
Fix import
beerose Mar 14, 2023
82b75f0
Remove unused import
beerose Mar 14, 2023
56170f6
Merge branch 'master' into typed-document-string
beerose Mar 14, 2023
e3120b3
Update @graphql-typed-document-node/core version
beerose Mar 14, 2023
8b5ad73
Merge branch 'master' into typed-document-string
beerose Mar 15, 2023
f960ccf
chore(dependencies): updated changesets for modified dependencies
github-actions[bot] Mar 15, 2023
ed34330
Update react-query example — use fetch insetad of http executor
beerose Mar 15, 2023
0283daf
Merge branch 'master' into typed-document-string
beerose Mar 15, 2023
cfd9380
Add docs
beerose Mar 15, 2023
a97276b
Update @graphql-typed-document-node/code version
beerose Mar 20, 2023
2e99784
chore(dependencies): updated changesets for modified dependencies
github-actions[bot] Mar 20, 2023
bdc1e7d
Add more info to the dosc
beerose Mar 20, 2023
e954bea
Merge branch 'master' into typed-document-string
beerose Mar 20, 2023
0f155b4
Update docs
beerose Mar 21, 2023
5771e6d
Update changeset
beerose Mar 21, 2023
5ce6b4f
Update versions in changeset
beerose Mar 21, 2023
a86f4bc
Merge branch 'master' into typed-document-string
beerose Mar 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions examples/react/urql-string-document-mode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want a new example or just modify an existing one?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think urql is not a good example because it actually requires the AST at runtime anyways 🤔 .
We can add or modify a react-query or similar example that does not require the AST.
What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think urql is not a good example because it actually requires the AST at runtime anyways

Does it, though? It accepts both string and AST and seems to work fine (I modified the original urql example). Unless I'm missing something.

Regarding the react-query example — it currently uses @graphql-tools/executor-http, but I could replace it with fetch.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, urql uses graphql internally and thus will just parse the string to the ast at runtime

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I am fine with replacing executor HTTP in react-query!


# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
17 changes: 17 additions & 0 deletions examples/react/urql-string-document-mode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Using GraphQL Code Generator with URQL and React

This example illustrates using GraphQL Code Generator in a React application using the URQL GraphQL Client.

You will find the TypeScript-based codegen configuration in [`codegen.ts`](./codegen.ts).

This simple codegen configuration generates types and helpers in the [`src/gql`](./src/gql/) folder that help you to get typed GraphQL Queries and Mutations seamlessly ⚡️

<br />

For a step-by-step implementation tutorial, please refer to the related guide:

https://www.the-guild.dev/graphql/codegen/docs/guides/react-vue-angular

--

Please note that the `client` preset used in this example is compatible with `@urql/core` (since `1.15.0`), `@urql/preact` (since `1.4.0`) and `urql` (since `1.11.0`).
18 changes: 18 additions & 0 deletions examples/react/urql-string-document-mode/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable import/no-extraneous-dependencies */
import { type CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'https://swapi-graphql.netlify.app/.netlify/functions/index',
documents: ['src/**/*.tsx', '!src/gql/**/*'],
generates: {
'./src/gql/': {
preset: 'client',
config: {
documentMode: 'string',
},
},
},
hooks: { afterAllFileWrite: ['prettier --write'] },
};

export default config;
10 changes: 10 additions & 0 deletions examples/react/urql-string-document-mode/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable import/no-extraneous-dependencies */
import { defineConfig } from 'cypress';

export default defineConfig({
e2e: {
setupNodeEvents(_on, _config) {
// implement node event listeners here
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
describe('template spec', () => {
it('renders everything correctly', () => {
cy.visit('http://localhost:3000');
cy.get('h3').should('contain', 'A New Hope');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="cypress" />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './commands';
13 changes: 13 additions & 0 deletions examples/react/urql-string-document-mode/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
30 changes: 30 additions & 0 deletions examples/react/urql-string-document-mode/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "example-react-urql-string-document-node",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"urql": "^3.0.0"
},
"devDependencies": {
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.10",
"@graphql-codegen/cli": "^3.2.2",
"@graphql-codegen/client-preset": "^2.1.1",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "4.9.5",
"serve": "14.2.0",
"cypress": "12.6.0",
"start-server-and-test": "2.0.0",
"vite": "^4.1.0"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "serve -s dist",
"test": "cypress run",
"test:end2end": "start-server-and-test start http://localhost:3000 test",
"codegen": "graphql-codegen --config codegen.ts"
}
}
1 change: 1 addition & 0 deletions examples/react/urql-string-document-mode/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions examples/react/urql-string-document-mode/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
62 changes: 62 additions & 0 deletions examples/react/urql-string-document-mode/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { TypedDocumentString } from '@graphql-typed-document-node/core';

import './App.css';
import Film from './Film';
import { graphql } from './gql';
import { AnyVariables, OperationContext, RequestPolicy, TypedDocumentNode, useQuery, UseQueryResponse } from 'urql';
import type { DocumentNode } from 'graphql';

const allFilmsWithVariablesQueryDocument = graphql(/* GraphQL */ `
query allFilmsWithVariablesQuery($first: Int!) {
allFilms(first: $first) {
edges {
node {
...FilmItem
}
}
}
}
`);

declare module 'urql' {
// @ts-expect-error this is just temporary until we update types in urql
beerose marked this conversation as resolved.
Show resolved Hide resolved
export type UseQueryArgs<Variables extends AnyVariables = AnyVariables, Data = any> = {
query: string | DocumentNode | TypedDocumentNode<Data, Variables> | TypedDocumentString<Data, Variables>;
requestPolicy?: RequestPolicy;
context?: Partial<OperationContext>;
pause?: boolean;
} & (Variables extends void
? {
variables?: Variables;
}
: Variables extends {
[P in keyof Variables]: Variables[P] | null;
}
? {
variables?: Variables;
}
: {
variables: Variables;
});

export function useQuery<Data = any, Variables extends AnyVariables = AnyVariables>(
args: UseQueryArgs<Variables, Data>
): UseQueryResponse<Data, Variables>;
}

function App() {
const [{ data }] = useQuery({
query: allFilmsWithVariablesQueryDocument,
variables: {
first: 10,
},
});

return (
<div className="App">
{data && <ul>{data.allFilms?.edges?.map((e, i) => e?.node && <Film film={e?.node} key={`film-${i}`} />)}</ul>}
</div>
);
}

export default App;
26 changes: 26 additions & 0 deletions examples/react/urql-string-document-mode/src/Film.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FragmentType, useFragment } from './gql/fragment-masking';
import { graphql } from './gql';

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

const Film = (props: {
/* tweet property has the correct type 🎉 */
film: FragmentType<typeof FilmFragment>;
}) => {
const film = useFragment(FilmFragment, props.film);
return (
<div>
<h3>{film.title}</h3>
<p>{film.releaseDate}</p>
</div>
);
};

export default Film;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ResultOf, DocumentTypeDecoration } from '@graphql-typed-document-node/core';

export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> =
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<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
): TType;
// return nullable if `fragmentType` is nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
): TType | null | undefined;
// return array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
): ReadonlyArray<TType>;
// return array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): ReadonlyArray<TType> | null | undefined;
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| FragmentType<DocumentTypeDecoration<TType, any>>
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined
): TType | ReadonlyArray<TType> | null | undefined {
return fragmentType as any;
}

export function makeFragmentData<F extends DocumentTypeDecoration<any, any>, FT extends ResultOf<F>>(
data: FT,
_fragment: F
): FragmentType<F> {
return data as FragmentType<F>;
}
36 changes: 36 additions & 0 deletions examples/react/urql-string-document-mode/src/gql/gql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable */
import * as types from './graphql';

/**
* 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.
*/
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 import('./graphql').AllFilmsWithVariablesQueryDocument;
/**
* 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 import('./graphql').FilmItemFragmentDoc;

export function graphql(source: string) {
return (documents as any)[source] ?? {};
}
Loading