UNPKG

13.2 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7const utils = require('@graphql-tools/utils');
8const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common');
9const pluginHelpers = require('@graphql-codegen/plugin-helpers');
10const graphql = require('graphql');
11const autoBind = _interopDefault(require('auto-bind'));
12const typescript = require('@graphql-codegen/typescript');
13
14const ENUM_RESOLVERS_SIGNATURE = 'export type EnumResolverSignature<T, AllowedValues = any> = { [key in keyof T]?: AllowedValues };';
15class 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
89const 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 // eslint-disable-next-line no-console
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 // runs visitor
114 const visitorResult = graphql.visit(astNode, { leave: visitor });
115 const optionalSignForInfoArg = visitor.config.optionalInfoArgument ? '?' : '';
116 const prepend = [];
117 const defsToInclude = [];
118 const legacyStitchingResolverType = `
119export type LegacyStitchingResolver<TResult, TParent, TContext, TArgs> = {
120 fragment: string;
121 resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
122};`;
123 const newStitchingResolverType = `
124export 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 // Resolver = ResolverFn;
153 defsToInclude.push(`${resolverType} ${resolverFnUsage};`);
154 }
155 else {
156 // StitchingResolver
157 // Resolver =
158 // | ResolverFn
159 // | StitchingResolver;
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 = `
187export 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
201export 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
208export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
209 parent: TParent,
210 args: TArgs,
211 context: TContext,
212 info${optionalSignForInfoArg}: GraphQLResolveInfo
213) => TResult | Promise<TResult>;
214
215export 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
220export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
221 subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>;
222 resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>;
223}
224
225export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> =
226 | SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
227 | SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;
228
229export 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
233export type TypeResolveFn<TTypes, TParent = {}, TContext = {}> = (
234 parent: TParent,
235 context: TContext,
236 info${optionalSignForInfoArg}: GraphQLResolveInfo
237) => ${namespacedImportPrefix}Maybe<TTypes> | Promise<${namespacedImportPrefix}Maybe<TTypes>>;
238
239export type IsTypeOfResolverFn<T = {}> = (obj: T, info${optionalSignForInfoArg}: GraphQLResolveInfo) => boolean | Promise<boolean>;
240
241export type NextResolverFn<T> = () => Promise<T>;
242
243export 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 // eslint-disable-next-line no-console
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
290exports.TypeScriptResolversVisitor = TypeScriptResolversVisitor;
291exports.plugin = plugin;
292//# sourceMappingURL=index.cjs.js.map