1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
6 |
|
7 | const utils = require('@graphql-tools/utils');
|
8 | const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common');
|
9 | const pluginHelpers = require('@graphql-codegen/plugin-helpers');
|
10 | const graphql = require('graphql');
|
11 | const autoBind = _interopDefault(require('auto-bind'));
|
12 | const typescript = require('@graphql-codegen/typescript');
|
13 |
|
14 | const ENUM_RESOLVERS_SIGNATURE = 'export type EnumResolverSignature<T, AllowedValues = any> = { [key in keyof T]?: AllowedValues };';
|
15 | class TypeScriptResolversVisitor extends visitorPluginCommon.BaseResolversVisitor {
|
16 | constructor(pluginConfig, schema) {
|
17 | super(pluginConfig, {
|
18 | avoidOptionals: visitorPluginCommon.getConfigValue(pluginConfig.avoidOptionals, false),
|
19 | useIndexSignature: visitorPluginCommon.getConfigValue(pluginConfig.useIndexSignature, false),
|
20 | wrapFieldDefinitions: visitorPluginCommon.getConfigValue(pluginConfig.wrapFieldDefinitions, false),
|
21 | allowParentTypeOverride: visitorPluginCommon.getConfigValue(pluginConfig.allowParentTypeOverride, false),
|
22 | optionalInfoArgument: visitorPluginCommon.getConfigValue(pluginConfig.optionalInfoArgument, false),
|
23 | }, schema);
|
24 | autoBind(this);
|
25 | this.setVariablesTransformer(new typescript.TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals, this.config.immutableTypes, this.config.namespacedImportName, [], this.config.enumPrefix, this.config.enumValues));
|
26 | if (this.config.useIndexSignature) {
|
27 | this._declarationBlockConfig = {
|
28 | blockTransformer(block) {
|
29 | return `ResolversObject<${block}>`;
|
30 | },
|
31 | };
|
32 | }
|
33 | }
|
34 | transformParentGenericType(parentType) {
|
35 | if (this.config.allowParentTypeOverride) {
|
36 | return `ParentType = ${parentType}`;
|
37 | }
|
38 | return `ParentType extends ${parentType} = ${parentType}`;
|
39 | }
|
40 | formatRootResolver(schemaTypeName, resolverType, declarationKind) {
|
41 | return `${schemaTypeName}${this.config.avoidOptionals ? '' : '?'}: ${resolverType}${this.getPunctuation(declarationKind)}`;
|
42 | }
|
43 | clearOptional(str) {
|
44 | if (str.startsWith('Maybe')) {
|
45 | return str.replace(/Maybe<(.*?)>$/, '$1');
|
46 | }
|
47 | return str;
|
48 | }
|
49 | ListType(node) {
|
50 | return `Maybe<${super.ListType(node)}>`;
|
51 | }
|
52 | wrapWithListType(str) {
|
53 | return `${this.config.immutableTypes ? 'ReadonlyArray' : 'Array'}<${str}>`;
|
54 | }
|
55 | getParentTypeForSignature(node) {
|
56 | if (this._federation.isResolveReferenceField(node) && this.config.wrapFieldDefinitions) {
|
57 | return 'UnwrappedObject<ParentType>';
|
58 | }
|
59 | return 'ParentType';
|
60 | }
|
61 | NamedType(node) {
|
62 | return `Maybe<${super.NamedType(node)}>`;
|
63 | }
|
64 | NonNullType(node) {
|
65 | const baseValue = super.NonNullType(node);
|
66 | return this.clearOptional(baseValue);
|
67 | }
|
68 | getPunctuation(declarationKind) {
|
69 | return ';';
|
70 | }
|
71 | buildEnumResolverContentBlock(node, mappedEnumType) {
|
72 | const valuesMap = `{ ${(node.values || [])
|
73 | .map(v => `${v.name}${this.config.avoidOptionals ? '' : '?'}: any`)
|
74 | .join(', ')} }`;
|
75 | this._globalDeclarations.add(ENUM_RESOLVERS_SIGNATURE);
|
76 | return `EnumResolverSignature<${valuesMap}, ${mappedEnumType}>`;
|
77 | }
|
78 | buildEnumResolversExplicitMappedValues(node, valuesMapping) {
|
79 | return `{ ${(node.values || [])
|
80 | .map(v => {
|
81 | const valueName = v.name;
|
82 | const mappedValue = valuesMapping[valueName];
|
83 | return `${valueName}: ${typeof mappedValue === 'number' ? mappedValue : `'${mappedValue}'`}`;
|
84 | })
|
85 | .join(', ')} }`;
|
86 | }
|
87 | }
|
88 |
|
89 | const plugin = (schema, documents, config) => {
|
90 | const imports = [];
|
91 | if (!config.customResolveInfo) {
|
92 | imports.push('GraphQLResolveInfo');
|
93 | }
|
94 | const showUnusedMappers = typeof config.showUnusedMappers === 'boolean' ? config.showUnusedMappers : true;
|
95 | const noSchemaStitching = typeof config.noSchemaStitching === 'boolean' ? config.noSchemaStitching : false;
|
96 | if (config.noSchemaStitching === false) {
|
97 |
|
98 | console.warn(`The default behavior of 'noSchemaStitching' will be reversed in the next major release. Support for Schema Stitching will be disabled by default.`);
|
99 | }
|
100 | const indexSignature = config.useIndexSignature
|
101 | ? [
|
102 | 'export type WithIndex<TObject> = TObject & Record<string, any>;',
|
103 | 'export type ResolversObject<TObject> = WithIndex<TObject>;',
|
104 | ].join('\n')
|
105 | : '';
|
106 | const transformedSchema = config.federation ? pluginHelpers.addFederationReferencesToSchema(schema) : schema;
|
107 | const visitor = new TypeScriptResolversVisitor(config, transformedSchema);
|
108 | const namespacedImportPrefix = visitor.config.namespacedImportName ? `${visitor.config.namespacedImportName}.` : '';
|
109 | const printedSchema = config.federation
|
110 | ? utils.printSchemaWithDirectives(transformedSchema)
|
111 | : graphql.printSchema(transformedSchema);
|
112 | const astNode = graphql.parse(printedSchema);
|
113 |
|
114 | const visitorResult = graphql.visit(astNode, { leave: visitor });
|
115 | const optionalSignForInfoArg = visitor.config.optionalInfoArgument ? '?' : '';
|
116 | const prepend = [];
|
117 | const defsToInclude = [];
|
118 | const legacyStitchingResolverType = `
|
119 | export type LegacyStitchingResolver<TResult, TParent, TContext, TArgs> = {
|
120 | fragment: string;
|
121 | resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
|
122 | };`;
|
123 | const newStitchingResolverType = `
|
124 | export type NewStitchingResolver<TResult, TParent, TContext, TArgs> = {
|
125 | selectionSet: string;
|
126 | resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
|
127 | };`;
|
128 | const stitchingResolverType = `export type StitchingResolver<TResult, TParent, TContext, TArgs> = LegacyStitchingResolver<TResult, TParent, TContext, TArgs> | NewStitchingResolver<TResult, TParent, TContext, TArgs>;`;
|
129 | const resolverType = `export type Resolver<TResult, TParent = {}, TContext = {}, TArgs = {}> =`;
|
130 | const resolverFnUsage = `ResolverFn<TResult, TParent, TContext, TArgs>`;
|
131 | const stitchingResolverUsage = `StitchingResolver<TResult, TParent, TContext, TArgs>`;
|
132 | if (visitor.hasFederation()) {
|
133 | if (visitor.config.wrapFieldDefinitions) {
|
134 | defsToInclude.push(`export type UnwrappedObject<T> = {
|
135 | [P in keyof T]: T[P] extends infer R | Promise<infer R> | (() => infer R2 | Promise<infer R2>)
|
136 | ? R & R2 : T[P]
|
137 | };`);
|
138 | }
|
139 | defsToInclude.push(`export type ReferenceResolver<TResult, TReference, TContext> = (
|
140 | reference: TReference,
|
141 | context: TContext,
|
142 | info${optionalSignForInfoArg}: GraphQLResolveInfo
|
143 | ) => Promise<TResult> | TResult;`);
|
144 | defsToInclude.push(`
|
145 | type ScalarCheck<T, S> = S extends true ? T : NullableCheck<T, S>;
|
146 | type NullableCheck<T, S> = Maybe<T> extends T ? Maybe<ListCheck<NonNullable<T>, S>> : ListCheck<T, S>;
|
147 | type ListCheck<T, S> = T extends (infer U)[] ? NullableCheck<U, S>[] : GraphQLRecursivePick<T, S>;
|
148 | export type GraphQLRecursivePick<T, S> = { [K in keyof T & keyof S]: ScalarCheck<T[K], S[K]> };
|
149 | `);
|
150 | }
|
151 | if (noSchemaStitching) {
|
152 |
|
153 | defsToInclude.push(`${resolverType} ${resolverFnUsage};`);
|
154 | }
|
155 | else {
|
156 |
|
157 |
|
158 |
|
159 |
|
160 | defsToInclude.push([
|
161 | legacyStitchingResolverType,
|
162 | newStitchingResolverType,
|
163 | stitchingResolverType,
|
164 | resolverType,
|
165 | ` | ${resolverFnUsage}`,
|
166 | ` | ${stitchingResolverUsage};`,
|
167 | ].join('\n'));
|
168 | }
|
169 | if (config.customResolverFn) {
|
170 | const parsedMapper = visitorPluginCommon.parseMapper(config.customResolverFn);
|
171 | if (parsedMapper.isExternal) {
|
172 | const importType = config.useTypeImports ? 'import type' : 'import';
|
173 | if (parsedMapper.default) {
|
174 | prepend.push(`${importType} ResolverFn from '${parsedMapper.source}';`);
|
175 | }
|
176 | else {
|
177 | prepend.push(`${importType} { ${parsedMapper.import} ${parsedMapper.import !== 'ResolverFn' ? 'as ResolverFn ' : ''}} from '${parsedMapper.source}';`);
|
178 | }
|
179 | prepend.push(`export${config.useTypeImports ? ' type' : ''} { ResolverFn };`);
|
180 | }
|
181 | else {
|
182 | prepend.push(`export type ResolverFn<TResult, TParent, TContext, TArgs> = ${parsedMapper.type}`);
|
183 | }
|
184 | }
|
185 | else {
|
186 | const defaultResolverFn = `
|
187 | export type ResolverFn<TResult, TParent, TContext, TArgs> = (
|
188 | parent: TParent,
|
189 | args: TArgs,
|
190 | context: TContext,
|
191 | info${optionalSignForInfoArg}: GraphQLResolveInfo
|
192 | ) => Promise<TResult> | TResult;`;
|
193 | defsToInclude.push(defaultResolverFn);
|
194 | }
|
195 | const header = `${indexSignature}
|
196 |
|
197 | ${visitor.getResolverTypeWrapperSignature()}
|
198 |
|
199 | ${defsToInclude.join('\n')}
|
200 |
|
201 | export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = (
|
202 | parent: TParent,
|
203 | args: TArgs,
|
204 | context: TContext,
|
205 | info${optionalSignForInfoArg}: GraphQLResolveInfo
|
206 | ) => AsyncIterator<TResult> | Promise<AsyncIterator<TResult>>;
|
207 |
|
208 | export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
|
209 | parent: TParent,
|
210 | args: TArgs,
|
211 | context: TContext,
|
212 | info${optionalSignForInfoArg}: GraphQLResolveInfo
|
213 | ) => TResult | Promise<TResult>;
|
214 |
|
215 | export interface SubscriptionSubscriberObject<TResult, TKey extends string, TParent, TContext, TArgs> {
|
216 | subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>;
|
217 | resolve?: SubscriptionResolveFn<TResult, { [key in TKey]: TResult }, TContext, TArgs>;
|
218 | }
|
219 |
|
220 | export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
|
221 | subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>;
|
222 | resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>;
|
223 | }
|
224 |
|
225 | export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> =
|
226 | | SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
|
227 | | SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;
|
228 |
|
229 | export type SubscriptionResolver<TResult, TKey extends string, TParent = {}, TContext = {}, TArgs = {}> =
|
230 | | ((...args: any[]) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
|
231 | | SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>;
|
232 |
|
233 | export type TypeResolveFn<TTypes, TParent = {}, TContext = {}> = (
|
234 | parent: TParent,
|
235 | context: TContext,
|
236 | info${optionalSignForInfoArg}: GraphQLResolveInfo
|
237 | ) => ${namespacedImportPrefix}Maybe<TTypes> | Promise<${namespacedImportPrefix}Maybe<TTypes>>;
|
238 |
|
239 | export type IsTypeOfResolverFn<T = {}> = (obj: T, info${optionalSignForInfoArg}: GraphQLResolveInfo) => boolean | Promise<boolean>;
|
240 |
|
241 | export type NextResolverFn<T> = () => Promise<T>;
|
242 |
|
243 | export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs = {}> = (
|
244 | next: NextResolverFn<TResult>,
|
245 | parent: TParent,
|
246 | args: TArgs,
|
247 | context: TContext,
|
248 | info${optionalSignForInfoArg}: GraphQLResolveInfo
|
249 | ) => TResult | Promise<TResult>;
|
250 | `;
|
251 | const resolversTypeMapping = visitor.buildResolversTypes();
|
252 | const resolversParentTypeMapping = visitor.buildResolversParentTypes();
|
253 | const { getRootResolver, getAllDirectiveResolvers, mappersImports, unusedMappers, hasScalars } = visitor;
|
254 | if (hasScalars()) {
|
255 | imports.push('GraphQLScalarType', 'GraphQLScalarTypeConfig');
|
256 | }
|
257 | if (showUnusedMappers && unusedMappers.length) {
|
258 |
|
259 | console.warn(`Unused mappers: ${unusedMappers.join(',')}`);
|
260 | }
|
261 | if (imports.length) {
|
262 | prepend.push(`import { ${imports.join(', ')} } from 'graphql';`);
|
263 | }
|
264 | if (config.customResolveInfo) {
|
265 | const parsedMapper = visitorPluginCommon.parseMapper(config.customResolveInfo);
|
266 | if (parsedMapper.isExternal) {
|
267 | if (parsedMapper.default) {
|
268 | prepend.push(`import GraphQLResolveInfo from '${parsedMapper.source}'`);
|
269 | }
|
270 | prepend.push(`import { ${parsedMapper.import} ${parsedMapper.import !== 'GraphQLResolveInfo' ? 'as GraphQLResolveInfo' : ''} } from '${parsedMapper.source}';`);
|
271 | }
|
272 | else {
|
273 | prepend.push(`type GraphQLResolveInfo = ${parsedMapper.type}`);
|
274 | }
|
275 | }
|
276 | prepend.push(...mappersImports, ...visitor.globalDeclarations);
|
277 | return {
|
278 | prepend,
|
279 | content: [
|
280 | header,
|
281 | resolversTypeMapping,
|
282 | resolversParentTypeMapping,
|
283 | ...visitorResult.definitions.filter(d => typeof d === 'string'),
|
284 | getRootResolver(),
|
285 | getAllDirectiveResolvers(),
|
286 | ].join('\n'),
|
287 | };
|
288 | };
|
289 |
|
290 | exports.TypeScriptResolversVisitor = TypeScriptResolversVisitor;
|
291 | exports.plugin = plugin;
|
292 |
|