-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
index.ts
109 lines (94 loc) · 3.44 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { PluginFunction } from '@graphql-codegen/plugin-helpers';
import { FragmentDefinitionNode, OperationDefinitionNode } from 'graphql';
import { Source } from '@graphql-tools/utils';
export type OperationOrFragment = {
initialName: string;
definition: OperationDefinitionNode | FragmentDefinitionNode;
};
export type SourceWithOperations = {
source: Source;
operations: Array<OperationOrFragment>;
};
const documentTypePartial = `
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode<
infer TType,
any
>
? TType
: never;
`.split(`\n`);
export const plugin: PluginFunction<{
sourcesWithOperations: Array<SourceWithOperations>;
useTypeImports?: boolean;
augmentedModuleName?: string;
emitLegacyCommonJSImports?: boolean;
}> = (_, __, { sourcesWithOperations, useTypeImports, augmentedModuleName, emitLegacyCommonJSImports }, _info) => {
if (!sourcesWithOperations) {
return '';
}
if (augmentedModuleName == null) {
return [
`import * as graphql from './graphql${emitLegacyCommonJSImports ? '' : '.js'}';\n`,
`${
useTypeImports ? 'import type' : 'import'
} { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\n`,
`\n`,
...getDocumentRegistryChunk(sourcesWithOperations),
`\n`,
...getGqlOverloadChunk(sourcesWithOperations, 'lookup', emitLegacyCommonJSImports),
`\n`,
`export function gql(source: string): unknown;\n`,
`export function gql(source: string) {\n`,
` return (documents as any)[source] ?? {};\n`,
`}\n`,
`\n`,
...documentTypePartial,
].join(``);
}
return [
`import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\n`,
`declare module "${augmentedModuleName}" {`,
[
`\n`,
...getGqlOverloadChunk(sourcesWithOperations, 'augmented', emitLegacyCommonJSImports),
`export function gql(source: string): unknown;\n`,
`\n`,
...documentTypePartial,
]
.map(line => (line === `\n` ? line : ` ${line}`))
.join(``),
`}`,
].join(`\n`);
};
function getDocumentRegistryChunk(sourcesWithOperations: Array<SourceWithOperations> = []) {
const lines = new Set<string>();
lines.add(`const documents = {\n`);
for (const { operations, ...rest } of sourcesWithOperations) {
const originalString = rest.source.rawSDL!;
const operation = operations[0];
lines.add(` ${JSON.stringify(originalString)}: graphql.${operation.initialName},\n`);
}
lines.add(`};\n`);
return lines;
}
type Mode = 'lookup' | 'augmented';
function getGqlOverloadChunk(
sourcesWithOperations: Array<SourceWithOperations>,
mode: Mode,
emitLegacyCommonJSImports?: boolean
) {
const lines = new Set<string>();
// We intentionally don't use a <T extends keyof typeof documents> generic, because TS
// would print very long `gql` function signatures (duplicating the source).
for (const { operations, ...rest } of sourcesWithOperations) {
const originalString = rest.source.rawSDL!;
const returnType =
mode === 'lookup'
? `(typeof documents)[${JSON.stringify(originalString)}]`
: emitLegacyCommonJSImports
? `typeof import('./graphql').${operations[0].initialName}`
: `typeof import('./graphql.js').${operations[0].initialName}`;
lines.add(`export function gql(source: ${JSON.stringify(originalString)}): ${returnType};\n`);
}
return lines;
}