Skip to content

Commit 164fac4

Browse files
committed
wip: typed-document-node plugin
1 parent b913dd6 commit 164fac4

File tree

6 files changed

+149
-1
lines changed

6 files changed

+149
-1
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
2+
import gql from 'graphql-tag';
3+
import { GraphQLTypes, InputType, ValueTypes, Zeus } from './index';
4+
5+
// Note: turns out the generated query does NOT have any params :|
6+
// type ParamsType<SRC> = SRC extends [infer Params, infer Result] ? Params : { [K in keyof SRC]: ParamsType<SRC[K]> };
7+
8+
// type PayloadType<SRC, DST> = MapType<SRC, IsPayload<DST>>;
9+
10+
export function createTypedQuery<Z extends ValueTypes['query_root']>(
11+
query: Z,
12+
): TypedDocumentNode<InputType<GraphQLTypes['query_root'], Z>, {}> {
13+
const zeusQuery = Zeus('query', query);
14+
const gqlQuery = gql(zeusQuery);
15+
16+
return gqlQuery;
17+
}
18+
19+
// Example
20+
const userMemberships = createTypedQuery({
21+
user: [
22+
{
23+
id: 'x',
24+
},
25+
{
26+
memberships: [
27+
{
28+
limit: 5,
29+
},
30+
{
31+
role: true,
32+
},
33+
],
34+
},
35+
],
36+
});

examples/typescript-node/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"dependencies": {
1616
"@apollo/client": "^3.4.16",
1717
"node-fetch": "^2.6.0",
18-
"react-query": "^3.27.0"
18+
"react-query": "^3.27.0",
19+
"@graphql-typed-document-node/core":"^3.1.1",
20+
"graphql-tag":"^2.12.6"
1921
}
2022
}

src/CLI/CLIClass.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Parser } from 'graphql-js-tree';
88
import { pluginApollo } from '@/plugins/apollo';
99
import { pluginReactQuery } from '@/plugins/react-query';
1010
import { pluginStucco } from '@/plugins/stuccoSubscriptions';
11+
import { pluginTypedDocumentNode } from '@/plugins/typed-document-node';
1112

1213
/**
1314
* basic yargs interface
@@ -96,6 +97,9 @@ export class CLI {
9697
if (args.stuccoSubscriptions) {
9798
writeFileRecursive(path.join(pathToFile, 'zeus'), `stuccoSubscriptions.ts`, pluginStucco({ tree }).ts);
9899
}
100+
if (args.typedDocumentNode) {
101+
writeFileRecursive(path.join(pathToFile, 'zeus'), `typedDocumentNode.ts`, pluginTypedDocumentNode({ tree }).ts);
102+
}
99103
};
100104
}
101105

src/CLI/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env node
22
import * as yargs from 'yargs';
3+
import { boolean } from 'yargs';
34
import { CLI } from './CLIClass';
45
const args = yargs
56
.usage(
@@ -41,6 +42,11 @@ zeus [path] [output_path] [options]
4142
describe: 'Generate React Query useTypedQuery module',
4243
boolean: true,
4344
})
45+
.option('typedDocumentNode', {
46+
alias: 'td',
47+
describe: 'Generate TypedDocumentNode createQuery module',
48+
boolean: true,
49+
})
4450
.option('header', {
4551
alias: 'h',
4652
describe:
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Parser } from 'graphql-js-tree';
2+
import { pluginTypedDocumentNode } from '.';
3+
4+
describe('plugin typed document node test', () => {
5+
it('generates correct apollo plugin from the schema', () => {
6+
const schema = `
7+
type Query{
8+
people: [String!]!
9+
}
10+
type Mutation{
11+
register(name: String!): String!
12+
}
13+
type Subscription{
14+
registrations: [String!]!
15+
}
16+
schema{
17+
query: Query
18+
mutation: Mutation
19+
subscription: Subscription
20+
}
21+
`;
22+
const tree = Parser.parse(schema);
23+
const tdnResult = pluginTypedDocumentNode({ tree });
24+
expect(tdnResult.ts).toContain(`TODO`);
25+
});
26+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { OperationType, ParserTree } from 'graphql-js-tree';
2+
3+
const pluginApolloOps = ({ queryName, operation }: { queryName: string; operation: OperationType | 'LazyQuery' }) => {
4+
const capitalized = operation[0].toUpperCase() + operation.slice(1);
5+
const zeusOperation = operation === 'LazyQuery' ? OperationType.query : operation;
6+
7+
return {
8+
queryName,
9+
operation,
10+
ts: `export function useTyped${capitalized}<Z extends ValueTypes[O], O extends "${queryName}">(
11+
${operation}: Z | ValueTypes[O],
12+
options?: ${capitalized}HookOptions<InputType<GraphQLTypes[O], Z>>,
13+
operationName?: string,
14+
) {
15+
return use${capitalized}<InputType<GraphQLTypes[O], Z>>(gql(Zeus("${zeusOperation}",${operation}, operationName)), options);
16+
}`,
17+
};
18+
};
19+
20+
export const pluginTypedDocumentNode = ({ tree, esModule }: { tree: ParserTree; esModule?: boolean }) => {
21+
const operationNodes = tree.nodes.filter((n) => n.type.operations);
22+
const opsFunctions = operationNodes.flatMap((n) =>
23+
n.type.operations!.map((o) => pluginApolloOps({ queryName: n.name, operation: o })),
24+
);
25+
for (const [index, o] of opsFunctions.entries()) {
26+
if (o.operation === OperationType.query) {
27+
opsFunctions.splice(index + 1, 0, pluginApolloOps({ queryName: o.queryName, operation: 'LazyQuery' }));
28+
break;
29+
}
30+
}
31+
const o = opsFunctions.reduce<Pick<ReturnType<typeof pluginApolloOps>, 'ts'>>(
32+
(a, b) => {
33+
a.ts = [a.ts, b.ts].join('\n');
34+
return a;
35+
},
36+
{ ts: '' },
37+
);
38+
const capitalizedOps = opsFunctions.map((o) => o.operation[0].toUpperCase() + o.operation.slice(1));
39+
const jsDefsImports: string[] = [];
40+
if (capitalizedOps.includes('LazyQuery')) {
41+
jsDefsImports.push('QueryTuple');
42+
}
43+
if (capitalizedOps.includes('Query')) {
44+
jsDefsImports.push('QueryResult');
45+
}
46+
if (capitalizedOps.includes('Mutation')) {
47+
jsDefsImports.push('MutationTuple');
48+
}
49+
return {
50+
ts: `/* eslint-disable */
51+
52+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
53+
import gql from 'graphql-tag';
54+
import { GraphQLTypes, InputType, Selectors, ValueTypes, Zeus } from './index${esModule ? '.js' : ''}';
55+
56+
export const constructQuery = <T extends ValueTypes['Query']>(q: T) => {
57+
const gqlString = Zeus.query(q);
58+
const selector = Selectors.query(q);
59+
type InferredResponseType = InputType<GraphQLTypes['Query'], typeof selector>;
60+
return gql(gqlString) as TypedDocumentNode<InferredResponseType, {}>;
61+
};
62+
63+
const drawCardDocument = constructQuery({
64+
drawCard: {
65+
Attack: true,
66+
Children: true,
67+
id: true,
68+
},
69+
});
70+
71+
${o.ts}
72+
`,
73+
};
74+
};

0 commit comments

Comments
 (0)