/
fragment-masking-plugin.ts
88 lines (79 loc) · 3.15 KB
/
fragment-masking-plugin.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
import type { PluginFunction } from '@graphql-codegen/plugin-helpers';
const fragmentTypeHelper = `
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;`;
const defaultUnmaskFunctionName = 'useFragment';
const modifyType = (rawType: string, opts: { nullable: boolean; list: 'with-list' | 'only-list' | false }) =>
`${
opts.list === 'only-list'
? `ReadonlyArray<${rawType}>`
: opts.list === 'with-list'
? `${rawType} | ReadonlyArray<${rawType}>`
: rawType
}${opts.nullable ? ' | null | undefined' : ''}`;
const createUnmaskFunctionTypeDefinition = (
unmaskFunctionName = defaultUnmaskFunctionName,
opts: { nullable: boolean; list: 'with-list' | 'only-list' | false }
) => `export function ${unmaskFunctionName}<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: ${modifyType('FragmentType<DocumentNode<TType, any>>', opts)}
): ${modifyType('TType', opts)}`;
const createUnmaskFunctionTypeDefinitions = (unmaskFunctionName = defaultUnmaskFunctionName) => [
`// return non-nullable if \`fragmentType\` is non-nullable\n${createUnmaskFunctionTypeDefinition(
unmaskFunctionName,
{ nullable: false, list: false }
)}`,
`// return nullable if \`fragmentType\` is nullable\n${createUnmaskFunctionTypeDefinition(unmaskFunctionName, {
nullable: true,
list: false,
})}`,
`// return array of non-nullable if \`fragmentType\` is array of non-nullable\n${createUnmaskFunctionTypeDefinition(
unmaskFunctionName,
{ nullable: false, list: 'only-list' }
)}`,
`// return array of nullable if \`fragmentType\` is array of nullable\n${createUnmaskFunctionTypeDefinition(
unmaskFunctionName,
{ nullable: true, list: 'only-list' }
)}`,
];
const createUnmaskFunction = (unmaskFunctionName = defaultUnmaskFunctionName) => `
${createUnmaskFunctionTypeDefinitions(unmaskFunctionName).join(';\n')}
${createUnmaskFunctionTypeDefinition(unmaskFunctionName, { nullable: true, list: 'with-list' })} {
return fragmentType as any
}
`;
/**
* Plugin for generating fragment masking helper functions.
*/
export const plugin: PluginFunction<{
useTypeImports?: boolean;
augmentedModuleName?: string;
unmaskFunctionName?: string;
}> = (_, __, { useTypeImports, augmentedModuleName, unmaskFunctionName }, _info) => {
const documentNodeImport = `${
useTypeImports ? 'import type' : 'import'
} { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\n`;
if (augmentedModuleName == null) {
return [documentNodeImport, `\n`, fragmentTypeHelper, `\n`, createUnmaskFunction(unmaskFunctionName)].join(``);
}
return [
documentNodeImport,
`declare module "${augmentedModuleName}" {`,
[
...fragmentTypeHelper.split(`\n`),
`\n`,
...createUnmaskFunctionTypeDefinitions(unmaskFunctionName).join('\n').split('\n'),
]
.map(line => (line === `\n` || line === '' ? line : ` ${line}`))
.join(`\n`),
`}`,
].join(`\n`);
};