1 | import { oldVisit } from '@graphql-codegen/plugin-helpers';
|
2 | import { isEnumType, isNonNullType, concatAST, Kind } from 'graphql';
|
3 | import { BaseSelectionSetProcessor, BaseDocumentsVisitor, getConfigValue, normalizeAvoidOptionals, wrapTypeWithModifiers, PreResolveTypesProcessor, SelectionSetToObject, generateFragmentImportStatement, optimizeOperations } from '@graphql-codegen/visitor-plugin-common';
|
4 | import autoBind from 'auto-bind';
|
5 | import { TypeScriptOperationVariablesToObject as TypeScriptOperationVariablesToObject$1 } from '@graphql-codegen/typescript';
|
6 |
|
7 | class TypeScriptOperationVariablesToObject extends TypeScriptOperationVariablesToObject$1 {
|
8 | formatTypeString(fieldType, isNonNullType, _hasDefaultValue) {
|
9 | return fieldType;
|
10 | }
|
11 | }
|
12 |
|
13 | class TypeScriptSelectionSetProcessor extends BaseSelectionSetProcessor {
|
14 | transformPrimitiveFields(schemaType, fields) {
|
15 | if (fields.length === 0) {
|
16 | return [];
|
17 | }
|
18 | const parentName = (this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '') +
|
19 | this.config.convertName(schemaType.name, {
|
20 | useTypesPrefix: true,
|
21 | });
|
22 | let hasConditionals = false;
|
23 | const conditilnalsList = [];
|
24 | let resString = `Pick<${parentName}, ${fields
|
25 | .map(field => {
|
26 | if (field.isConditional) {
|
27 | hasConditionals = true;
|
28 | conditilnalsList.push(field.fieldName);
|
29 | }
|
30 | return `'${field.fieldName}'`;
|
31 | })
|
32 | .join(' | ')}>`;
|
33 | if (hasConditionals) {
|
34 | const avoidOptional =
|
35 |
|
36 | this.config.avoidOptionals === true ||
|
37 | this.config.avoidOptionals.field ||
|
38 | this.config.avoidOptionals.inputValue ||
|
39 | this.config.avoidOptionals.object;
|
40 | const transform = avoidOptional ? 'MakeMaybe' : 'MakeOptional';
|
41 | resString = `${this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : ''}${transform}<${resString}, ${conditilnalsList.map(field => `'${field}'`).join(' | ')}>`;
|
42 | }
|
43 | return [resString];
|
44 | }
|
45 | transformTypenameField(type, name) {
|
46 | return [`{ ${name}: ${type} }`];
|
47 | }
|
48 | transformAliasesPrimitiveFields(schemaType, fields) {
|
49 | if (fields.length === 0) {
|
50 | return [];
|
51 | }
|
52 | const parentName = (this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '') +
|
53 | this.config.convertName(schemaType.name, {
|
54 | useTypesPrefix: true,
|
55 | });
|
56 | return [
|
57 | `{ ${fields
|
58 | .map(aliasedField => {
|
59 | const value = aliasedField.fieldName === '__typename'
|
60 | ? `'${schemaType.name}'`
|
61 | : `${parentName}['${aliasedField.fieldName}']`;
|
62 | return `${aliasedField.alias}: ${value}`;
|
63 | })
|
64 | .join(', ')} }`,
|
65 | ];
|
66 | }
|
67 | transformLinkFields(fields) {
|
68 | if (fields.length === 0) {
|
69 | return [];
|
70 | }
|
71 | return [`{ ${fields.map(field => `${field.alias || field.name}: ${field.selectionSet}`).join(', ')} }`];
|
72 | }
|
73 | }
|
74 |
|
75 | class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor {
|
76 | constructor(schema, config, allFragments) {
|
77 | super(config, {
|
78 | arrayInputCoercion: getConfigValue(config.arrayInputCoercion, true),
|
79 | noExport: getConfigValue(config.noExport, false),
|
80 | avoidOptionals: normalizeAvoidOptionals(getConfigValue(config.avoidOptionals, false)),
|
81 | immutableTypes: getConfigValue(config.immutableTypes, false),
|
82 | nonOptionalTypename: getConfigValue(config.nonOptionalTypename, false),
|
83 | preResolveTypes: getConfigValue(config.preResolveTypes, true),
|
84 | mergeFragmentTypes: getConfigValue(config.mergeFragmentTypes, false),
|
85 | }, schema);
|
86 | autoBind(this);
|
87 | const preResolveTypes = getConfigValue(config.preResolveTypes, true);
|
88 | const defaultMaybeValue = 'T | null';
|
89 | const maybeValue = getConfigValue(config.maybeValue, defaultMaybeValue);
|
90 | const wrapOptional = (type) => {
|
91 | if (preResolveTypes === true) {
|
92 | return maybeValue.replace('T', type);
|
93 | }
|
94 | const prefix = this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '';
|
95 | return `${prefix}Maybe<${type}>`;
|
96 | };
|
97 | const wrapArray = (type) => {
|
98 | const listModifier = this.config.immutableTypes ? 'ReadonlyArray' : 'Array';
|
99 | return `${listModifier}<${type}>`;
|
100 | };
|
101 | const formatNamedField = (name, type, isConditional = false) => {
|
102 | const optional = isConditional || (!this.config.avoidOptionals.field && !!type && !isNonNullType(type));
|
103 | return (this.config.immutableTypes ? `readonly ${name}` : name) + (optional ? '?' : '');
|
104 | };
|
105 | const processorConfig = {
|
106 | namespacedImportName: this.config.namespacedImportName,
|
107 | convertName: this.convertName.bind(this),
|
108 | enumPrefix: this.config.enumPrefix,
|
109 | scalars: this.scalars,
|
110 | formatNamedField,
|
111 | wrapTypeWithModifiers(baseType, type) {
|
112 | return wrapTypeWithModifiers(baseType, type, { wrapOptional, wrapArray });
|
113 | },
|
114 | avoidOptionals: this.config.avoidOptionals,
|
115 | };
|
116 | const processor = new (preResolveTypes ? PreResolveTypesProcessor : TypeScriptSelectionSetProcessor)(processorConfig);
|
117 | this.setSelectionSetHandler(new SelectionSetToObject(processor, this.scalars, this.schema, this.convertName.bind(this), this.getFragmentSuffix.bind(this), allFragments, this.config));
|
118 | const enumsNames = Object.keys(schema.getTypeMap()).filter(typeName => isEnumType(schema.getType(typeName)));
|
119 | this.setVariablesTransformer(new TypeScriptOperationVariablesToObject(this.scalars, this.convertName.bind(this), this.config.avoidOptionals.object, this.config.immutableTypes, this.config.namespacedImportName, enumsNames, this.config.enumPrefix, this.config.enumValues, this.config.arrayInputCoercion, undefined, 'InputMaybe'));
|
120 | this._declarationBlockConfig = {
|
121 | ignoreExport: this.config.noExport,
|
122 | };
|
123 | }
|
124 | getImports() {
|
125 | return !this.config.globalNamespace &&
|
126 | (this.config.inlineFragmentTypes === 'combine' || this.config.inlineFragmentTypes === 'mask')
|
127 | ? this.config.fragmentImports.map(fragmentImport => generateFragmentImportStatement(fragmentImport, 'type'))
|
128 | : [];
|
129 | }
|
130 | getPunctuation(_declarationKind) {
|
131 | return ';';
|
132 | }
|
133 | applyVariablesWrapper(variablesBlock) {
|
134 | const prefix = this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '';
|
135 | return `${prefix}Exact<${variablesBlock === '{}' ? `{ [key: string]: never; }` : variablesBlock}>`;
|
136 | }
|
137 | }
|
138 |
|
139 | const plugin = (schema, rawDocuments, config) => {
|
140 | const documents = config.flattenGeneratedTypes
|
141 | ? optimizeOperations(schema, rawDocuments, {
|
142 | includeFragments: config.flattenGeneratedTypesIncludeFragments,
|
143 | })
|
144 | : rawDocuments;
|
145 | const allAst = concatAST(documents.map(v => v.document));
|
146 | const allFragments = [
|
147 | ...allAst.definitions.filter(d => d.kind === Kind.FRAGMENT_DEFINITION).map(fragmentDef => ({
|
148 | node: fragmentDef,
|
149 | name: fragmentDef.name.value,
|
150 | onType: fragmentDef.typeCondition.name.value,
|
151 | isExternal: false,
|
152 | })),
|
153 | ...(config.externalFragments || []),
|
154 | ];
|
155 | const visitor = new TypeScriptDocumentsVisitor(schema, config, allFragments);
|
156 | const visitorResult = oldVisit(allAst, {
|
157 | leave: visitor,
|
158 | });
|
159 | let content = visitorResult.definitions.join('\n');
|
160 | if (config.addOperationExport) {
|
161 | const exportConsts = [];
|
162 | allAst.definitions.forEach(d => {
|
163 | if ('name' in d) {
|
164 | exportConsts.push(`export declare const ${d.name.value}: import("graphql").DocumentNode;`);
|
165 | }
|
166 | });
|
167 | content = visitorResult.definitions.concat(exportConsts).join('\n');
|
168 | }
|
169 | if (config.globalNamespace) {
|
170 | content = `
|
171 | declare global {
|
172 | ${content}
|
173 | }`;
|
174 | }
|
175 | return {
|
176 | prepend: [...visitor.getImports(), ...visitor.getGlobalDeclarations(visitor.config.noExport)],
|
177 | content,
|
178 | };
|
179 | };
|
180 |
|
181 | export { TypeScriptDocumentsVisitor, plugin };
|