Skip to content

Commit 1a64f6d

Browse files
authored
Merge branch 'master' into federation-fixes
2 parents 0b0c9bf + c8a7fef commit 1a64f6d

File tree

30 files changed

+526
-71
lines changed

30 files changed

+526
-71
lines changed

.changeset/old-gorillas-taste.md

-6
This file was deleted.

examples/programmatic-typescript/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
"dependencies": {
1313
"@graphql-codegen/core": "4.0.2",
1414
"@graphql-codegen/plugin-helpers": "5.1.0",
15-
"@graphql-codegen/typed-document-node": "5.1.0",
16-
"@graphql-codegen/typescript": "4.1.5",
17-
"@graphql-codegen/typescript-operations": "4.5.1",
18-
"@graphql-codegen/typescript-resolvers": "4.4.4",
15+
"@graphql-codegen/typed-document-node": "5.1.1",
16+
"@graphql-codegen/typescript": "4.1.6",
17+
"@graphql-codegen/typescript-operations": "4.6.0",
18+
"@graphql-codegen/typescript-resolvers": "4.5.0",
1919
"@graphql-tools/graphql-file-loader": "8.0.1",
2020
"@graphql-tools/load": "8.0.2",
2121
"@graphql-tools/schema": "10.0.6",

examples/typescript-resolvers/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"private": true,
55
"devDependencies": {
66
"@graphql-codegen/cli": "5.0.5",
7-
"@graphql-codegen/typescript": "4.1.5",
8-
"@graphql-codegen/typescript-resolvers": "4.4.4"
7+
"@graphql-codegen/typescript": "4.1.6",
8+
"@graphql-codegen/typescript-resolvers": "4.5.0"
99
},
1010
"dependencies": {
1111
"graphql": "16.9.0",

packages/plugins/other/visitor-plugin-common/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @graphql-codegen/visitor-plugin-common
22

3+
## 5.8.0
4+
5+
### Minor Changes
6+
7+
- [#10315](https://github.com/dotansimha/graphql-code-generator/pull/10315) [`f6909d1`](https://github.com/dotansimha/graphql-code-generator/commit/f6909d1797c15b79a0afb7ec089471763a485bfc) Thanks [@eddeee888](https://github.com/eddeee888)! - Implement semanticNonNull custom directive
8+
39
## 5.7.1
410

511
### Patch Changes

packages/plugins/other/visitor-plugin-common/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@graphql-codegen/visitor-plugin-common",
3-
"version": "5.7.1",
3+
"version": "5.8.0",
44
"license": "MIT",
55
"repository": {
66
"type": "git",

packages/plugins/typescript/document-nodes/CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @graphql-codegen/typescript-document-nodes
22

3+
## 4.0.16
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`f6909d1`](https://github.com/dotansimha/graphql-code-generator/commit/f6909d1797c15b79a0afb7ec089471763a485bfc)]:
8+
- @graphql-codegen/visitor-plugin-common@5.8.0
9+
310
## 4.0.15
411

512
### Patch Changes

packages/plugins/typescript/document-nodes/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@graphql-codegen/typescript-document-nodes",
3-
"version": "4.0.15",
3+
"version": "4.0.16",
44
"description": "GraphQL Code Generator plugin for generating TypeScript modules with embedded GraphQL document nodes",
55
"repository": {
66
"type": "git",
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@graphql-codegen/plugin-helpers": "^5.1.0",
17-
"@graphql-codegen/visitor-plugin-common": "5.7.1",
17+
"@graphql-codegen/visitor-plugin-common": "5.8.0",
1818
"auto-bind": "~4.0.0",
1919
"tslib": "~2.6.0"
2020
},

packages/plugins/typescript/gql-tag-operations/CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @graphql-codegen/gql-tag-operations
22

3+
## 4.0.17
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [[`f6909d1`](https://github.com/dotansimha/graphql-code-generator/commit/f6909d1797c15b79a0afb7ec089471763a485bfc)]:
8+
- @graphql-codegen/visitor-plugin-common@5.8.0
9+
310
## 4.0.16
411

512
### Patch Changes

packages/plugins/typescript/gql-tag-operations/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@graphql-codegen/gql-tag-operations",
3-
"version": "4.0.16",
3+
"version": "4.0.17",
44
"description": "GraphQL Code Generator plugin for generating a typed gql tag function",
55
"repository": {
66
"type": "git",
@@ -18,7 +18,7 @@
1818
"dependencies": {
1919
"@graphql-tools/utils": "^10.0.0",
2020
"@graphql-codegen/plugin-helpers": "^5.1.0",
21-
"@graphql-codegen/visitor-plugin-common": "5.7.1",
21+
"@graphql-codegen/visitor-plugin-common": "5.8.0",
2222
"auto-bind": "~4.0.0",
2323
"tslib": "~2.6.0"
2424
},

packages/plugins/typescript/operations/CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# @graphql-codegen/typescript-operations
22

3+
## 4.6.0
4+
5+
### Minor Changes
6+
7+
- [#10323](https://github.com/dotansimha/graphql-code-generator/pull/10323) [`f3cf4df`](https://github.com/dotansimha/graphql-code-generator/commit/f3cf4df358a896c5df0a7d8909c2fbf192e10c01) Thanks [@eddeee888](https://github.com/eddeee888)! - Add support for `nullability.errorHandlingClient`. This allows clients to get stronger types with [semantic nullability](https://github.com/graphql/graphql-wg/blob/main/rfcs/SemanticNullability.md)-enabled schemas.
8+
9+
### Patch Changes
10+
11+
- Updated dependencies [[`f6909d1`](https://github.com/dotansimha/graphql-code-generator/commit/f6909d1797c15b79a0afb7ec089471763a485bfc)]:
12+
- @graphql-codegen/visitor-plugin-common@5.8.0
13+
- @graphql-codegen/typescript@4.1.6
14+
315
## 4.5.1
416

517
### Patch Changes

packages/plugins/typescript/operations/package.json

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@graphql-codegen/typescript-operations",
3-
"version": "4.5.1",
3+
"version": "4.6.0",
44
"description": "GraphQL Code Generator plugin for generating TypeScript types for GraphQL queries, mutations, subscriptions and fragments",
55
"repository": {
66
"type": "git",
@@ -14,13 +14,17 @@
1414
},
1515
"dependencies": {
1616
"@graphql-codegen/plugin-helpers": "^5.1.0",
17-
"@graphql-codegen/typescript": "^4.1.5",
18-
"@graphql-codegen/visitor-plugin-common": "5.7.1",
17+
"@graphql-codegen/typescript": "^4.1.6",
18+
"@graphql-codegen/visitor-plugin-common": "5.8.0",
1919
"auto-bind": "~4.0.0",
2020
"tslib": "~2.6.0"
2121
},
2222
"peerDependencies": {
23-
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
23+
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
24+
"graphql-sock": "^1.0.0"
25+
},
26+
"devDependencies": {
27+
"graphql-sock": "1.0.0"
2428
},
2529
"main": "dist/cjs/index.js",
2630
"module": "dist/esm/index.js",

packages/plugins/typescript/operations/src/config.ts

+44
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,48 @@ export interface TypeScriptDocumentsPluginConfig extends RawDocumentsConfig {
292292
*/
293293

294294
allowUndefinedQueryVariables?: boolean;
295+
296+
/**
297+
* @description Options related to handling nullability
298+
* @exampleMarkdown
299+
* ## `errorHandlingClient`
300+
* An error handling client is a client which prevents the user from reading a `null` used as a placeholder for an error in a GraphQL response.
301+
* The client may do so by throwing when an errored field is accessed (as is the case for [`graphql-toe`](https://github.com/graphile/graphql-toe)),
302+
* or when a fragment containing an error is read (as is the case for Relay's `@throwOnFieldError` directive),
303+
* or by preventing any data from being read if an error occurred (as with Apollo Client's `errorPolicy: "none"`).
304+
*
305+
* When using error handling clients, a semantic non-nullable field can never be `null`.
306+
* If a semantic non-nullable field's value in the response is `null`, there must be a respective error.
307+
* The error handling client will throw in this case, so the `null` value is never read.
308+
*
309+
* To enable this option, install `graphql-sock` peer dependency:
310+
*
311+
* ```sh npm2yarn
312+
* npm install -D graphql-sock
313+
* ```
314+
*
315+
* Now, you can enable support for error handling clients:
316+
*
317+
* ```ts filename="codegen.ts"
318+
* import type { CodegenConfig } from '@graphql-codegen/cli';
319+
*
320+
* const config: CodegenConfig = {
321+
* // ...
322+
* generates: {
323+
* 'path/to/file.ts': {
324+
* plugins: ['typescript', 'typescript-operations'],
325+
* config: {
326+
* nullability: {
327+
* errorHandlingClient: true
328+
* }
329+
* },
330+
* },
331+
* },
332+
* };
333+
* export default config;
334+
* ```
335+
*/
336+
nullability?: {
337+
errorHandlingClient: boolean;
338+
};
295339
}

packages/plugins/typescript/operations/src/index.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { TypeScriptDocumentsVisitor } from './visitor.js';
66

77
export { TypeScriptDocumentsPluginConfig } from './config.js';
88

9-
export const plugin: PluginFunction<TypeScriptDocumentsPluginConfig, Types.ComplexPluginOutput> = (
10-
schema: GraphQLSchema,
9+
export const plugin: PluginFunction<TypeScriptDocumentsPluginConfig, Types.ComplexPluginOutput> = async (
10+
inputSchema: GraphQLSchema,
1111
rawDocuments: Types.DocumentFile[],
1212
config: TypeScriptDocumentsPluginConfig
1313
) => {
14+
const schema = config.nullability?.errorHandlingClient ? await semanticToStrict(inputSchema) : inputSchema;
15+
1416
const documents = config.flattenGeneratedTypes
1517
? optimizeOperations(schema, rawDocuments, {
1618
includeFragments: config.flattenGeneratedTypesIncludeFragments,
@@ -64,3 +66,14 @@ export const plugin: PluginFunction<TypeScriptDocumentsPluginConfig, Types.Compl
6466
};
6567

6668
export { TypeScriptDocumentsVisitor };
69+
70+
const semanticToStrict = async (schema: GraphQLSchema): Promise<GraphQLSchema> => {
71+
try {
72+
const sock = await import('graphql-sock');
73+
return sock.semanticToStrict(schema);
74+
} catch {
75+
throw new Error(
76+
"To use the `nullability.errorHandlingClient` option, you must install the 'graphql-sock' package."
77+
);
78+
}
79+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { buildSchema, parse } from 'graphql';
2+
import * as prettier from 'prettier';
3+
import { plugin } from '../src/index.js';
4+
5+
const schema = buildSchema(/* GraphQL */ `
6+
directive @semanticNonNull(levels: [Int] = [0]) on FIELD_DEFINITION
7+
8+
type Query {
9+
me: User
10+
}
11+
12+
type User {
13+
field: String @semanticNonNull
14+
fieldLevel0: String @semanticNonNull(levels: [0])
15+
fieldLevel1: String @semanticNonNull(levels: [1])
16+
fieldBothLevels: String @semanticNonNull(levels: [0, 1])
17+
list: [String] @semanticNonNull
18+
listLevel0: [String] @semanticNonNull(levels: [0])
19+
listLevel1: [String] @semanticNonNull(levels: [1])
20+
listBothLevels: [String] @semanticNonNull(levels: [0, 1])
21+
nonNullableList: [String]! @semanticNonNull
22+
nonNullableListLevel0: [String]! @semanticNonNull(levels: [0])
23+
nonNullableListLevel1: [String]! @semanticNonNull(levels: [1])
24+
nonNullableListBothLevels: [String]! @semanticNonNull(levels: [0, 1])
25+
listWithNonNullableItem: [String!] @semanticNonNull
26+
listWithNonNullableItemLevel0: [String!] @semanticNonNull(levels: [0])
27+
listWithNonNullableItemLevel1: [String!] @semanticNonNull(levels: [1])
28+
listWithNonNullableItemBothLevels: [String!] @semanticNonNull(levels: [0, 1])
29+
nonNullableListWithNonNullableItem: [String!]! @semanticNonNull
30+
nonNullableListWithNonNullableItemLevel0: [String!]! @semanticNonNull(levels: [0])
31+
nonNullableListWithNonNullableItemLevel1: [String!]! @semanticNonNull(levels: [1])
32+
nonNullableListWithNonNullableItemBothLevels: [String!]! @semanticNonNull(levels: [0, 1])
33+
}
34+
`);
35+
36+
const document = parse(/* GraphQL */ `
37+
query {
38+
me {
39+
field
40+
fieldLevel0
41+
fieldLevel1
42+
fieldBothLevels
43+
list
44+
listLevel0
45+
listLevel1
46+
listBothLevels
47+
nonNullableList
48+
nonNullableListLevel0
49+
nonNullableListLevel1
50+
nonNullableListBothLevels
51+
listWithNonNullableItem
52+
listWithNonNullableItemLevel0
53+
listWithNonNullableItemLevel1
54+
listWithNonNullableItemBothLevels
55+
nonNullableListWithNonNullableItem
56+
nonNullableListWithNonNullableItemLevel0
57+
nonNullableListWithNonNullableItemLevel1
58+
nonNullableListWithNonNullableItemBothLevels
59+
}
60+
}
61+
`);
62+
63+
describe('TypeScript Operations Plugin - nullability', () => {
64+
it('converts semanticNonNull to nonNull when nullability.errorHandlingClient=true', async () => {
65+
const result = await plugin(schema, [{ document }], {
66+
nullability: {
67+
errorHandlingClient: true,
68+
},
69+
});
70+
71+
const formattedContent = prettier.format(result.content, { parser: 'typescript' });
72+
expect(formattedContent).toMatchInlineSnapshot(`
73+
"export type Unnamed_1_QueryVariables = Exact<{ [key: string]: never }>;
74+
75+
export type Unnamed_1_Query = {
76+
__typename?: "Query";
77+
me?: {
78+
__typename?: "User";
79+
field: string;
80+
fieldLevel0: string;
81+
fieldLevel1?: string | null;
82+
fieldBothLevels: string;
83+
list: Array<string | null>;
84+
listLevel0: Array<string | null>;
85+
listLevel1?: Array<string> | null;
86+
listBothLevels: Array<string>;
87+
nonNullableList: Array<string | null>;
88+
nonNullableListLevel0: Array<string | null>;
89+
nonNullableListLevel1: Array<string>;
90+
nonNullableListBothLevels: Array<string>;
91+
listWithNonNullableItem: Array<string>;
92+
listWithNonNullableItemLevel0: Array<string>;
93+
listWithNonNullableItemLevel1?: Array<string> | null;
94+
listWithNonNullableItemBothLevels: Array<string>;
95+
nonNullableListWithNonNullableItem: Array<string>;
96+
nonNullableListWithNonNullableItemLevel0: Array<string>;
97+
nonNullableListWithNonNullableItemLevel1: Array<string>;
98+
nonNullableListWithNonNullableItemBothLevels: Array<string>;
99+
} | null;
100+
};
101+
"
102+
`);
103+
});
104+
105+
it('does not convert nullability to nonNull when nullability.errorHandlingClient=false', async () => {
106+
const result = await plugin(schema, [{ document }], {
107+
nullability: {
108+
errorHandlingClient: false,
109+
},
110+
});
111+
112+
const formattedContent = prettier.format(result.content, { parser: 'typescript' });
113+
expect(formattedContent).toMatchInlineSnapshot(`
114+
"export type Unnamed_1_QueryVariables = Exact<{ [key: string]: never }>;
115+
116+
export type Unnamed_1_Query = {
117+
__typename?: "Query";
118+
me?: {
119+
__typename?: "User";
120+
field?: string | null;
121+
fieldLevel0?: string | null;
122+
fieldLevel1?: string | null;
123+
fieldBothLevels?: string | null;
124+
list?: Array<string | null> | null;
125+
listLevel0?: Array<string | null> | null;
126+
listLevel1?: Array<string | null> | null;
127+
listBothLevels?: Array<string | null> | null;
128+
nonNullableList: Array<string | null>;
129+
nonNullableListLevel0: Array<string | null>;
130+
nonNullableListLevel1: Array<string | null>;
131+
nonNullableListBothLevels: Array<string | null>;
132+
listWithNonNullableItem?: Array<string> | null;
133+
listWithNonNullableItemLevel0?: Array<string> | null;
134+
listWithNonNullableItemLevel1?: Array<string> | null;
135+
listWithNonNullableItemBothLevels?: Array<string> | null;
136+
nonNullableListWithNonNullableItem: Array<string>;
137+
nonNullableListWithNonNullableItemLevel0: Array<string>;
138+
nonNullableListWithNonNullableItemLevel1: Array<string>;
139+
nonNullableListWithNonNullableItemBothLevels: Array<string>;
140+
} | null;
141+
};
142+
"
143+
`);
144+
});
145+
});

0 commit comments

Comments
 (0)