Skip to content

Commit

Permalink
Load codegen.ts in ESM projects (#9086)
Browse files Browse the repository at this point in the history
* Add failing test

* Compile config file for esm projects

* Move comment

* Make sure example app works; add tests

* Add TypeScript to optional peer deps

* Add caching for transpiled files

* chore(dependencies): updated changesets for modified dependencies

* Simplify code a bit

* Add changeset

* Support .mts and .cts config extensions

* Use jiti insetad of custom cache and tsc

* Support mts and cts with jiti

* chore(dependencies): updated changesets for modified dependencies

* Update graphql-config

* chore(dependencies): updated changesets for modified dependencies

* Remove cosmiconfig-typescript-loader

* chore(dependencies): updated changesets for modified dependencies

* Use import over require

* chore(dependencies): updated changesets for modified dependencies

* Remove ts-node from graphql-codegen-cli

* chore(dependencies): updated changesets for modified dependencies

* Pass empty string to jiti

* Delete .eslintignore

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Dimitri POSTOLOV <dmytropostolov@gmail.com>
  • Loading branch information
3 people authored Mar 7, 2023
1 parent 347f4c6 commit a34cef3
Show file tree
Hide file tree
Showing 60 changed files with 5,255 additions and 94 deletions.
8 changes: 8 additions & 0 deletions .changeset/@graphql-codegen_cli-9086-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@graphql-codegen/cli": patch
---
dependencies updates:
- Updated dependency [`graphql-config@^4.5.0` ↗︎](https://www.npmjs.com/package/graphql-config/v/4.5.0) (from `^4.4.0`, in `dependencies`)
- Added dependency [`jiti@^1.17.1` ↗︎](https://www.npmjs.com/package/jiti/v/1.17.1) (to `dependencies`)
- Removed dependency [`cosmiconfig-typescript-loader@^4.3.0` ↗︎](https://www.npmjs.com/package/cosmiconfig-typescript-loader/v/4.3.0) (from `dependencies`)
- Removed dependency [`ts-node@^10.9.1` ↗︎](https://www.npmjs.com/package/ts-node/v/10.9.1) (from `dependencies`)
5 changes: 5 additions & 0 deletions .changeset/perfect-moose-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-codegen/cli': patch
---

Support `codegen.ts` in ESM projects
24 changes: 24 additions & 0 deletions examples/vite/vite-react-cts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
15 changes: 15 additions & 0 deletions examples/vite/vite-react-cts/codegen.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* 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'],
generates: {
'./src/gql/': {
preset: 'client',
},
},
hooks: { afterAllFileWrite: ['prettier --write'] },
};

export default config;
10 changes: 10 additions & 0 deletions examples/vite/vite-react-cts/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
},
},
});
6 changes: 6 additions & 0 deletions examples/vite/vite-react-cts/cypress/e2e/end2end.cy.ts
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');
});
});
1 change: 1 addition & 0 deletions examples/vite/vite-react-cts/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="cypress" />
2 changes: 2 additions & 0 deletions examples/vite/vite-react-cts/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Import commands.js using ES2015 syntax:
import './commands';
13 changes: 13 additions & 0 deletions examples/vite/vite-react-cts/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 + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
45 changes: 45 additions & 0 deletions examples/vite/vite-react-cts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "example-vite-react-cts",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"start": "vite preview --port 3000",
"codegen": "graphql-codegen --config codegen.cts",
"test": "cypress run",
"test:end2end": "start-server-and-test start http://localhost:3000 test"
},
"dependencies": {
"@apollo/client": "^3.6.9",
"@graphql-typed-document-node/core": "3.1.2",
"@vitejs/plugin-react-swc": "^3.0.0",
"graphql": "16.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vite": "^4.1.0"
},
"devDependencies": {
"@graphql-codegen/cli": "3.2.1",
"@graphql-codegen/client-preset": "2.1.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"cypress": "12.6.0",
"start-server-and-test": "2.0.0",
"typescript": "^4.9.3"
},
"bob": false,
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
26 changes: 26 additions & 0 deletions examples/vite/vite-react-cts/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;
50 changes: 50 additions & 0 deletions examples/vite/vite-react-cts/src/gql/fragment-masking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ResultOf, TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';

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;

// return non-nullable if `fragmentType` is non-nullable
export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>>
): TType;
// return nullable if `fragmentType` is nullable
export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>> | null | undefined
): TType | null | undefined;
// return array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentNode<TType, any>>>
): ReadonlyArray<TType>;
// return array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentNode<TType, any>>> | null | undefined
): ReadonlyArray<TType> | null | undefined;
export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType:
| FragmentType<DocumentNode<TType, any>>
| ReadonlyArray<FragmentType<DocumentNode<TType, any>>>
| null
| undefined
): TType | ReadonlyArray<TType> | null | undefined {
return fragmentType as any;
}

export function makeFragmentData<F extends DocumentNode, FT extends ResultOf<F>>(
data: FT,
_fragment: F
): FragmentType<F> {
return data as FragmentType<F>;
}
58 changes: 58 additions & 0 deletions examples/vite/vite-react-cts/src/gql/gql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* 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 fragment FilmItem on Film {\n id\n title\n releaseDate\n producers\n }\n':
types.FilmItemFragmentDoc,
'\n query allFilmsWithVariablesQuery($first: Int!) {\n allFilms(first: $first) {\n edges {\n node {\n ...FilmItem\n }\n }\n }\n }\n':
types.AllFilmsWithVariablesQueryDocument,
};

/**
* 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 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'];
/**
* 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: string) {
return (documents as any)[source] ?? {};
}

export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode<
infer TType,
any
>
? TType
: never;
Loading

0 comments on commit a34cef3

Please sign in to comment.