Skip to content

Commit 0007cc0

Browse files
gibson042eddeee888
andauthored
Represent operation descriptions as JSDoc (#1282)
* feat(graphql-request): Represent operation descriptions as JSDoc * feat(generic-sdk): Represent operation descriptions as JSDoc * feat(jit-sdk): Represent operation descriptions as JSDoc * feat(react-apollo): Represent operation descriptions as JSDoc * chore(release): Add changeset * fixup! feat(graphql-request): Represent operation descriptions as JSDoc * fixup! feat(generic-sdk): Represent operation descriptions as JSDoc * fixup! feat(jit-sdk): Represent operation descriptions as JSDoc * fixup! feat(react-apollo): Represent operation descriptions as JSDoc * fixup! feat(graphql-request): Represent operation descriptions as JSDoc * fixup! feat(generic-sdk): Represent operation descriptions as JSDoc * fixup! feat(jit-sdk): Represent operation descriptions as JSDoc * fixup! feat(react-apollo): Represent operation descriptions as JSDoc * fixup! feat(graphql-request): Represent operation descriptions as JSDoc * fixup! feat(generic-sdk): Represent operation descriptions as JSDoc * fixup! feat(jit-sdk): Represent operation descriptions as JSDoc * fixup! feat(react-apollo): Represent operation descriptions as JSDoc * chore: Accommodate old versions of graphql without sacrificing test coverage This makes it easy to update tests once support for those versions is dropped. * Revert "chore: Accommodate old versions of graphql without sacrificing test coverage" This reverts commit 5de2072. * Set up Jest to handle v16-only tests * Add v16+ tests * Manually update snapshot link. Weird * Remove descriptiont test in files that run graphql v15 and v16 * Rebase and update tests --------- Co-authored-by: Eddy Nguyen <github@eddeee888.me>
1 parent 007abb3 commit 0007cc0

16 files changed

Lines changed: 379 additions & 11 deletions

File tree

.changeset/shy-lines-grab.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@graphql-codegen/typescript-graphql-request': minor
3+
'@graphql-codegen/typescript-react-apollo': minor
4+
'@graphql-codegen/typescript-generic-sdk': minor
5+
'@graphql-codegen/typescript-jit-sdk': minor
6+
---
7+
8+
Represent operation descriptions as JSDoc comments

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,6 @@ jobs:
110110
- name: Build
111111
run: yarn build
112112
- name: Test
113-
run: yarn test
113+
run: GRAPHQL_VERSION=${{matrix.graphql_version}} yarn test
114114
env:
115115
CI: true

jest.project.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const ROOT_DIR = __dirname;
55
const TSCONFIG = resolve(ROOT_DIR, 'tsconfig.json');
66
const tsconfig = require(TSCONFIG);
77
const CI = !!process.env.CI;
8+
const GRAPHQL_VERSION = process.env.GRAPHQL_VERSION || '16';
89

910
const { versionInfo } = require('graphql');
1011

@@ -37,6 +38,7 @@ module.exports = ({ dirname, projectMode = true }) => {
3738
collectCoverage: false,
3839
testTimeout: 20000,
3940
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', '!**/nhost/**'],
41+
testPathIgnorePatterns: GRAPHQL_VERSION === '16' ? [] : ['.*v16\+.*'],
4042
resolver: './node_modules/bob-the-bundler/jest-resolver.cjs',
4143
snapshotFormat: {
4244
escapeString: false,

packages/plugins/typescript/generic-sdk/src/visitor.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import autoBind from 'auto-bind';
2-
import { GraphQLSchema, Kind, OperationDefinitionNode, print } from 'graphql';
2+
import { GraphQLSchema, Kind, OperationDefinitionNode, print, StringValueNode } from 'graphql';
33
import {
44
ClientSideBasePluginConfig,
55
ClientSideBaseVisitor,
66
DocumentMode,
77
getConfigValue,
88
indentMultiline,
99
LoadedFragment,
10+
transformComment,
1011
} from '@graphql-codegen/visitor-plugin-common';
1112
import { RawGenericSdkPluginConfig } from './config.js';
1213

@@ -110,6 +111,8 @@ export class GenericSdkVisitor extends ClientSideBaseVisitor<
110111
const allPossibleActions = this._operationsToInclude
111112
.map(o => {
112113
const operationName = o.node.name.value;
114+
const operationDocComment =
115+
'description' in o.node ? transformComment(o.node.description as StringValueNode) : '';
113116
const optionalVariables =
114117
!o.node.variableDefinitions ||
115118
o.node.variableDefinitions.length === 0 ||
@@ -125,7 +128,7 @@ export class GenericSdkVisitor extends ClientSideBaseVisitor<
125128
const resultData = this.config.rawRequest
126129
? `ExecutionResult<${o.operationResultType}, E>`
127130
: o.operationResultType;
128-
return `${operationName}(variables${optionalVariables ? '?' : ''}: ${
131+
return `${operationDocComment}${operationName}(variables${optionalVariables ? '?' : ''}: ${
129132
o.operationVariablesTypes
130133
}, options?: C): ${returnType}<${resultData}> {
131134
return requester<${o.operationResultType}, ${
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`generic-sdk - GraphQL v16+ generates operation description correctly 1`] = `
4+
{
5+
"content": "
6+
export const FeedDocument = gql\`
7+
"""description (becomes JSDoc)"""
8+
query feed {
9+
feed {
10+
id
11+
commentCount
12+
repository {
13+
owner {
14+
avatar_url
15+
}
16+
}
17+
}
18+
}
19+
\`;
20+
export type Requester<C = {}> = <R, V>(doc: DocumentNode, vars?: V, options?: C) => Promise<R> | AsyncIterable<R>
21+
export function getSdk<C>(requester: Requester<C>) {
22+
return {
23+
/** description (becomes JSDoc) */
24+
feed(variables?: FeedQueryVariables, options?: C): Promise<FeedQuery> {
25+
return requester<FeedQuery, FeedQueryVariables>(FeedDocument, variables, options) as Promise<FeedQuery>;
26+
}
27+
};
28+
}
29+
export type Sdk = ReturnType<typeof getSdk>;",
30+
"prepend": [
31+
"import { DocumentNode } from 'graphql';",
32+
"import gql from 'graphql-tag';",
33+
],
34+
}
35+
`;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { buildClientSchema, extendSchema, parse } from 'graphql';
2+
import { plugin } from '../src/index.js';
3+
4+
describe('generic-sdk - GraphQL v16+', () => {
5+
it('generates operation description correctly', async () => {
6+
const schema = extendSchema(
7+
buildClientSchema(require('../../../../../dev-test/githunt/schema.json')),
8+
parse(/* GraphQL */ `
9+
directive @live on QUERY
10+
`),
11+
);
12+
13+
const result = await plugin(
14+
schema,
15+
[
16+
{
17+
location: '',
18+
document: parse(/* GraphQL */ `
19+
"""description (becomes JSDoc)"""
20+
query feed {
21+
feed {
22+
id
23+
commentCount
24+
repository {
25+
owner {
26+
avatar_url
27+
}
28+
}
29+
}
30+
}
31+
`),
32+
},
33+
],
34+
{},
35+
{ outputFile: 'graphql.ts' },
36+
);
37+
expect(result).toMatchSnapshot();
38+
});
39+
});

packages/plugins/typescript/graphql-request/src/visitor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import autoBind from 'auto-bind';
2-
import { GraphQLSchema, Kind, OperationDefinitionNode, print } from 'graphql';
2+
import { GraphQLSchema, Kind, OperationDefinitionNode, print, StringValueNode } from 'graphql';
33
import {
44
ClientSideBasePluginConfig,
55
ClientSideBaseVisitor,
66
DocumentMode,
77
getConfigValue,
88
indentMultiline,
99
LoadedFragment,
10+
transformComment,
1011
} from '@graphql-codegen/visitor-plugin-common';
1112
import { RawGraphQLRequestPluginConfig } from './config.js';
1213

@@ -116,6 +117,8 @@ export class GraphQLRequestVisitor extends ClientSideBaseVisitor<
116117
.map(o => {
117118
const operationType = o.node.operation;
118119
const operationName = o.node.name.value;
120+
const operationDocComment =
121+
'description' in o.node ? transformComment(o.node.description as StringValueNode) : '';
119122
const optionalVariables =
120123
!o.node.variableDefinitions ||
121124
o.node.variableDefinitions.length === 0 ||
@@ -130,7 +133,7 @@ export class GraphQLRequestVisitor extends ClientSideBaseVisitor<
130133
docArg = `${docVarName}String`;
131134
extraVariables.push(`const ${docArg} = print(${docVarName});`);
132135
}
133-
return `${operationName}(variables${optionalVariables ? '?' : ''}: ${
136+
return `${operationDocComment}${operationName}(variables${optionalVariables ? '?' : ''}: ${
134137
o.operationVariablesTypes
135138
}, requestHeaders?: GraphQLClientRequestHeaders): Promise<{ data: ${
136139
o.operationResultType
@@ -142,7 +145,7 @@ export class GraphQLRequestVisitor extends ClientSideBaseVisitor<
142145
}>(${docArg}, variables, {...requestHeaders, ...wrappedRequestHeaders}), '${operationName}', '${operationType}', variables);
143146
}`;
144147
}
145-
return `${operationName}(variables${optionalVariables ? '?' : ''}: ${
148+
return `${operationDocComment}${operationName}(variables${optionalVariables ? '?' : ''}: ${
146149
o.operationVariablesTypes
147150
}, requestHeaders?: GraphQLClientRequestHeaders, signal?: RequestInit['signal']): Promise<${o.operationResultType}> {
148151
return withWrapper((wrappedRequestHeaders) => client.request<${
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`graphql-request - GraphQL v16+ generates operation description correctly 1`] = `
4+
{
5+
"content": "
6+
export const FeedDocument = gql\`
7+
"""description (becomes JSDoc)"""
8+
query feed {
9+
feed {
10+
id
11+
commentCount
12+
repository {
13+
owner {
14+
avatar_url
15+
}
16+
}
17+
}
18+
}
19+
\`;
20+
21+
export type SdkFunctionWrapper = <T>(action: (requestHeaders?:Record<string, string>) => Promise<T>, operationName: string, operationType?: string, variables?: any) => Promise<T>;
22+
23+
24+
const defaultWrapper: SdkFunctionWrapper = (action, _operationName, _operationType, _variables) => action();
25+
26+
export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) {
27+
return {
28+
/** description (becomes JSDoc) */
29+
feed(variables?: FeedQueryVariables, requestHeaders?: GraphQLClientRequestHeaders, signal?: RequestInit['signal']): Promise<FeedQuery> {
30+
return withWrapper((wrappedRequestHeaders) => client.request<FeedQuery>({ document: FeedDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), 'feed', 'query', variables);
31+
}
32+
};
33+
}
34+
export type Sdk = ReturnType<typeof getSdk>;",
35+
"prepend": [
36+
"import { GraphQLClient, RequestOptions } from 'graphql-request';",
37+
"type GraphQLClientRequestHeaders = RequestOptions['requestHeaders'];",
38+
"import gql from 'graphql-tag';",
39+
],
40+
}
41+
`;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { buildClientSchema, parse } from 'graphql';
2+
import { plugin } from '../src/index.js';
3+
4+
describe('graphql-request - GraphQL v16+', () => {
5+
it('generates operation description correctly', async () => {
6+
const schema = buildClientSchema(require('../../../../../dev-test/githunt/schema.json'));
7+
8+
const content = await plugin(
9+
schema,
10+
[
11+
{
12+
location: '',
13+
document: parse(/* GraphQL */ `
14+
"""description (becomes JSDoc)"""
15+
query feed {
16+
feed {
17+
id
18+
commentCount
19+
repository {
20+
owner {
21+
avatar_url
22+
}
23+
}
24+
}
25+
}
26+
`),
27+
},
28+
],
29+
{},
30+
{ outputFile: 'graphql.tsx' },
31+
);
32+
33+
expect(content).toMatchSnapshot();
34+
});
35+
});

packages/plugins/typescript/jit-sdk/src/visitor.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import autoBind from 'auto-bind';
2-
import { GraphQLSchema, Kind, OperationDefinitionNode, print } from 'graphql';
2+
import { GraphQLSchema, Kind, OperationDefinitionNode, print, StringValueNode } from 'graphql';
33
import {
44
ClientSideBasePluginConfig,
55
ClientSideBaseVisitor,
66
DocumentMode,
77
indentMultiline,
88
LoadedFragment,
9+
transformComment,
910
} from '@graphql-codegen/visitor-plugin-common';
1011
import { RawJitSdkPluginConfig } from './config.js';
1112

@@ -83,6 +84,8 @@ export class JitSdkVisitor extends ClientSideBaseVisitor<
8384
let hasSubscription = false;
8485
for (const o of this._operationsToInclude) {
8586
const operationName = o.node.name.value;
87+
const operationDocComment =
88+
'description' in o.node ? transformComment(o.node.description as StringValueNode) : '';
8689
const compiledQueryVariableName = `${operationName}Compiled`;
8790
compiledQueries.push(
8891
indentMultiline(
@@ -115,7 +118,7 @@ if(!(isCompiledQuery(${compiledQueryVariableName}))) {
115118
}
116119
sdkMethods.push(
117120
indentMultiline(
118-
`async ${operationName}(variables${optionalVariables ? '?' : ''}: ${
121+
`${operationDocComment}async ${operationName}(variables${optionalVariables ? '?' : ''}: ${
119122
o.operationVariablesTypes
120123
}, context?: TOperationContext, root?: TOperationRoot): Promise<${returnType}> {
121124
const result = await ${compiledQueryVariableName}.${methodName}({

0 commit comments

Comments
 (0)