UNPKG

120 kBJavaScriptView Raw
1import { Kind, isScalarType, isEqualType, isListType, isNonNullType, isObjectType, isAbstractType, isEnumType, isInterfaceType, GraphQLObjectType, isUnionType, visit, print, SchemaMetaFieldDef, TypeMetaFieldDef } from 'graphql';
2import { resolveExternalModuleAndFn, DetailedError, ApolloFederation, getBaseType } from '@graphql-codegen/plugin-helpers';
3import { pascalCase } from 'pascal-case';
4import { resolve, relative, dirname, join, isAbsolute, extname, basename } from 'path';
5import parse from 'parse-filepath';
6import autoBind from 'auto-bind';
7import flatMap from 'array.prototype.flatmap';
8import { DepGraph } from 'dependency-graph';
9import gqlTag from 'graphql-tag';
10import { optimizeDocuments } from '@graphql-tools/relay-operation-optimizer';
11
12const DEFAULT_SCALARS = {
13 ID: 'string',
14 String: 'string',
15 Boolean: 'boolean',
16 Int: 'number',
17 Float: 'number',
18};
19
20function isExternalMapperType(m) {
21 return !!m.import;
22}
23var MapperKind;
24(function (MapperKind) {
25 MapperKind[MapperKind["Namespace"] = 0] = "Namespace";
26 MapperKind[MapperKind["Default"] = 1] = "Default";
27 MapperKind[MapperKind["Regular"] = 2] = "Regular";
28})(MapperKind || (MapperKind = {}));
29function prepareLegacy(mapper) {
30 const items = mapper.split('#');
31 const isNamespace = items.length === 3;
32 const isDefault = items[1].trim() === 'default' || items[1].startsWith('default ');
33 const hasAlias = items[1].includes(' as ');
34 return {
35 items,
36 isDefault,
37 isNamespace,
38 hasAlias,
39 };
40}
41function prepare(mapper) {
42 const [source, path] = mapper.split('#');
43 const isNamespace = path.includes('.');
44 const isDefault = path.trim() === 'default' || path.startsWith('default ');
45 const hasAlias = path.includes(' as ');
46 return {
47 items: isNamespace ? [source, ...path.split('.')] : [source, path],
48 isDefault,
49 isNamespace,
50 hasAlias,
51 };
52}
53function isLegacyMode(mapper) {
54 return mapper.split('#').length === 3;
55}
56function parseMapper(mapper, gqlTypeName = null, suffix) {
57 if (isExternalMapper(mapper)) {
58 const { isNamespace, isDefault, hasAlias, items } = isLegacyMode(mapper) ? prepareLegacy(mapper) : prepare(mapper);
59 const mapperKind = isNamespace
60 ? MapperKind.Namespace
61 : isDefault
62 ? MapperKind.Default
63 : MapperKind.Regular;
64 function handleAlias(isDefault = false) {
65 const [importedType, aliasType] = items[1].split(/\s+as\s+/);
66 const type = maybeSuffix(aliasType);
67 return {
68 importElement: isDefault ? type : `${importedType} as ${type}`,
69 type: type,
70 };
71 }
72 function maybeSuffix(type) {
73 if (suffix) {
74 return addSuffix(type, suffix);
75 }
76 return type;
77 }
78 function handle() {
79 switch (mapperKind) {
80 // ./my/module#Namespace#Identifier
81 case MapperKind.Namespace: {
82 const [, ns, identifier] = items;
83 return {
84 type: `${ns}.${identifier}`,
85 importElement: ns,
86 };
87 }
88 case MapperKind.Default: {
89 // ./my/module#default as alias
90 if (hasAlias) {
91 return handleAlias(true);
92 }
93 const type = maybeSuffix(`${gqlTypeName}`);
94 // ./my/module#default
95 return {
96 importElement: type,
97 type,
98 };
99 }
100 case MapperKind.Regular: {
101 // ./my/module#Identifier as alias
102 if (hasAlias) {
103 return handleAlias();
104 }
105 const identifier = items[1];
106 const type = maybeSuffix(identifier);
107 // ./my/module#Identifier
108 return {
109 type,
110 importElement: suffix ? `${identifier} as ${type}` : type,
111 };
112 }
113 }
114 }
115 const { type, importElement } = handle();
116 return {
117 default: isDefault,
118 isExternal: true,
119 source: items[0],
120 type,
121 import: importElement.replace(/<(.*?)>/g, ''),
122 };
123 }
124 return {
125 isExternal: false,
126 type: mapper,
127 };
128}
129function addSuffix(element, suffix) {
130 const generic = element.indexOf('<');
131 if (generic === -1) {
132 return `${element}${suffix}`;
133 }
134 return `${element.slice(0, generic)}${suffix}${element.slice(generic)}`;
135}
136function isExternalMapper(value) {
137 return value.includes('#') && !value.includes('"') && !value.includes("'");
138}
139function transformMappers(rawMappers, mapperTypeSuffix) {
140 const result = {};
141 Object.keys(rawMappers).forEach(gqlTypeName => {
142 const mapperDef = rawMappers[gqlTypeName];
143 const parsedMapper = parseMapper(mapperDef, gqlTypeName, mapperTypeSuffix);
144 result[gqlTypeName] = parsedMapper;
145 });
146 return result;
147}
148
149const getConfigValue = (value, defaultValue) => {
150 if (value === null || value === undefined) {
151 return defaultValue;
152 }
153 return value;
154};
155function quoteIfNeeded(array, joinWith = ' & ') {
156 if (array.length === 0) {
157 return '';
158 }
159 else if (array.length === 1) {
160 return array[0];
161 }
162 else {
163 return `(${array.join(joinWith)})`;
164 }
165}
166function block(array) {
167 return array && array.length !== 0 ? '{\n' + array.join('\n') + '\n}' : '';
168}
169function wrapWithSingleQuotes(value) {
170 if (typeof value === 'number' ||
171 (typeof value === 'string' && !isNaN(parseInt(value)) && parseFloat(value).toString() === value)) {
172 return `${value}`;
173 }
174 return `'${value}'`;
175}
176function breakLine(str) {
177 return str + '\n';
178}
179function indent(str, count = 1) {
180 return new Array(count).fill(' ').join('') + str;
181}
182function indentMultiline(str, count = 1) {
183 const indentation = new Array(count).fill(' ').join('');
184 const replaceWith = '\n' + indentation;
185 return indentation + str.replace(/\n/g, replaceWith);
186}
187function transformComment(comment, indentLevel = 0) {
188 if (!comment || comment === '') {
189 return '';
190 }
191 if (isStringValueNode(comment)) {
192 comment = comment.value;
193 }
194 comment = comment.split('*/').join('*\\/');
195 let lines = comment.split('\n');
196 if (lines.length === 1) {
197 return indent(`/** ${lines[0]} */\n`, indentLevel);
198 }
199 lines = ['/**', ...lines.map(line => ` * ${line}`), ' */\n'];
200 return lines.map(line => indent(line, indentLevel)).join('\n');
201}
202class DeclarationBlock {
203 constructor(_config) {
204 this._config = _config;
205 this._decorator = null;
206 this._export = false;
207 this._name = null;
208 this._kind = null;
209 this._methodName = null;
210 this._content = null;
211 this._block = null;
212 this._nameGenerics = null;
213 this._comment = null;
214 this._ignoreBlockWrapper = false;
215 this._config = {
216 blockWrapper: '',
217 blockTransformer: block => block,
218 enumNameValueSeparator: ':',
219 ...this._config,
220 };
221 }
222 withDecorator(decorator) {
223 this._decorator = decorator;
224 return this;
225 }
226 export(exp = true) {
227 if (!this._config.ignoreExport) {
228 this._export = exp;
229 }
230 return this;
231 }
232 asKind(kind) {
233 this._kind = kind;
234 return this;
235 }
236 withComment(comment) {
237 const nonEmptyComment = isStringValueNode(comment) ? !!comment.value : !!comment;
238 if (nonEmptyComment) {
239 this._comment = transformComment(comment, 0);
240 }
241 return this;
242 }
243 withMethodCall(methodName, ignoreBlockWrapper = false) {
244 this._methodName = methodName;
245 this._ignoreBlockWrapper = ignoreBlockWrapper;
246 return this;
247 }
248 withBlock(block) {
249 this._block = block;
250 return this;
251 }
252 withContent(content) {
253 this._content = content;
254 return this;
255 }
256 withName(name, generics = null) {
257 this._name = name;
258 this._nameGenerics = generics;
259 return this;
260 }
261 get string() {
262 let result = '';
263 if (this._decorator) {
264 result += this._decorator + '\n';
265 }
266 if (this._export) {
267 result += 'export ';
268 }
269 if (this._kind) {
270 let extra = '';
271 let name = '';
272 if (['type', 'const', 'var', 'let'].includes(this._kind)) {
273 extra = '= ';
274 }
275 if (this._name) {
276 name = this._name + (this._nameGenerics || '') + ' ';
277 }
278 result += this._kind + ' ' + name + extra;
279 }
280 if (this._block) {
281 if (this._content) {
282 result += this._content;
283 }
284 const blockWrapper = this._ignoreBlockWrapper ? '' : this._config.blockWrapper;
285 const before = '{' + blockWrapper;
286 const after = blockWrapper + '}';
287 const block = [before, this._block, after].filter(val => !!val).join('\n');
288 if (this._methodName) {
289 result += `${this._methodName}(${this._config.blockTransformer(block)})`;
290 }
291 else {
292 result += this._config.blockTransformer(block);
293 }
294 }
295 else if (this._content) {
296 result += this._content;
297 }
298 else if (this._kind) {
299 result += this._config.blockTransformer('{}');
300 }
301 return ((this._comment ? this._comment : '') +
302 result +
303 (this._kind === 'interface' || this._kind === 'enum' || this._kind === 'namespace' || this._kind === 'function'
304 ? ''
305 : ';') +
306 '\n');
307 }
308}
309function getBaseTypeNode(typeNode) {
310 if (typeNode.kind === Kind.LIST_TYPE || typeNode.kind === Kind.NON_NULL_TYPE) {
311 return getBaseTypeNode(typeNode.type);
312 }
313 return typeNode;
314}
315function convertNameParts(str, func, removeUnderscore = false) {
316 if (removeUnderscore) {
317 return func(str);
318 }
319 return str
320 .split('_')
321 .map(s => func(s))
322 .join('_');
323}
324function buildScalars(schema, scalarsMapping, defaultScalarsMapping = DEFAULT_SCALARS, defaultScalarType = 'any') {
325 const result = {};
326 Object.keys(defaultScalarsMapping).forEach(name => {
327 result[name] = parseMapper(defaultScalarsMapping[name]);
328 });
329 if (schema) {
330 const typeMap = schema.getTypeMap();
331 Object.keys(typeMap)
332 .map(typeName => typeMap[typeName])
333 .filter(type => isScalarType(type))
334 .map((scalarType) => {
335 const name = scalarType.name;
336 if (typeof scalarsMapping === 'string') {
337 const value = parseMapper(scalarsMapping + '#' + name, name);
338 result[name] = value;
339 }
340 else if (scalarsMapping && typeof scalarsMapping[name] === 'string') {
341 const value = parseMapper(scalarsMapping[name], name);
342 result[name] = value;
343 }
344 else if (scalarsMapping && scalarsMapping[name]) {
345 result[name] = {
346 isExternal: false,
347 type: JSON.stringify(scalarsMapping[name]),
348 };
349 }
350 else if (!defaultScalarsMapping[name]) {
351 result[name] = {
352 isExternal: false,
353 type: defaultScalarType,
354 };
355 }
356 });
357 }
358 else if (scalarsMapping) {
359 if (typeof scalarsMapping === 'string') {
360 throw new Error('Cannot use string scalars mapping when building without a schema');
361 }
362 Object.keys(scalarsMapping).forEach(name => {
363 if (typeof scalarsMapping[name] === 'string') {
364 const value = parseMapper(scalarsMapping[name], name);
365 result[name] = value;
366 }
367 else {
368 result[name] = {
369 isExternal: false,
370 type: JSON.stringify(scalarsMapping[name]),
371 };
372 }
373 });
374 }
375 return result;
376}
377function isStringValueNode(node) {
378 return node && typeof node === 'object' && node.kind === Kind.STRING;
379}
380function isRootType(type, schema) {
381 return (isEqualType(type, schema.getQueryType()) ||
382 isEqualType(type, schema.getMutationType()) ||
383 isEqualType(type, schema.getSubscriptionType()));
384}
385function getRootTypeNames(schema) {
386 return [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]
387 .filter(t => t)
388 .map(t => t.name);
389}
390function stripMapperTypeInterpolation(identifier) {
391 return identifier.trim().replace(/<{.*}>/, '');
392}
393const OMIT_TYPE = 'export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;';
394const REQUIRE_FIELDS_TYPE = `export type RequireFields<T, K extends keyof T> = { [X in Exclude<keyof T, K>]?: T[X] } & { [P in K]-?: NonNullable<T[P]> };`;
395function mergeSelectionSets(selectionSet1, selectionSet2) {
396 const newSelections = [...selectionSet1.selections];
397 for (const selection2 of selectionSet2.selections) {
398 if (selection2.kind === 'FragmentSpread') {
399 newSelections.push(selection2);
400 continue;
401 }
402 if (selection2.kind !== 'Field') {
403 throw new TypeError('Invalid state.');
404 }
405 const match = newSelections.find(selection1 => selection1.kind === 'Field' && getFieldNodeNameValue(selection1) === getFieldNodeNameValue(selection2));
406 if (match) {
407 // recursively merge all selection sets
408 if (match.kind === 'Field' && match.selectionSet && selection2.selectionSet) {
409 mergeSelectionSets(match.selectionSet, selection2.selectionSet);
410 }
411 continue;
412 }
413 newSelections.push(selection2);
414 }
415 // replace existing selections
416 selectionSet1.selections = newSelections;
417}
418const getFieldNodeNameValue = (node) => {
419 return (node.alias || node.name).value;
420};
421function separateSelectionSet(selections) {
422 return {
423 fields: selections.filter(s => s.kind === Kind.FIELD),
424 inlines: selections.filter(s => s.kind === Kind.INLINE_FRAGMENT),
425 spreads: selections.filter(s => s.kind === Kind.FRAGMENT_SPREAD),
426 };
427}
428function getPossibleTypes(schema, type) {
429 if (isListType(type) || isNonNullType(type)) {
430 return getPossibleTypes(schema, type.ofType);
431 }
432 else if (isObjectType(type)) {
433 return [type];
434 }
435 else if (isAbstractType(type)) {
436 return schema.getPossibleTypes(type);
437 }
438 return [];
439}
440function wrapTypeWithModifiers(baseType, type, options) {
441 let currentType = type;
442 const modifiers = [];
443 while (currentType) {
444 if (isNonNullType(currentType)) {
445 currentType = currentType.ofType;
446 }
447 else {
448 modifiers.push(options.wrapOptional);
449 }
450 if (isListType(currentType)) {
451 modifiers.push(options.wrapArray);
452 currentType = currentType.ofType;
453 }
454 else {
455 break;
456 }
457 }
458 return modifiers.reduceRight((result, modifier) => modifier(result), baseType);
459}
460
461function getKind(node) {
462 if (typeof node === 'string') {
463 return 'typeNames';
464 }
465 if (['EnumValueDefinition', 'EnumValue'].includes(node.kind)) {
466 return 'enumValues';
467 }
468 return 'typeNames';
469}
470function getName(node) {
471 if (typeof node === 'string') {
472 return node;
473 }
474 switch (node.kind) {
475 case 'OperationDefinition':
476 case 'Variable':
477 case 'Argument':
478 case 'FragmentSpread':
479 case 'FragmentDefinition':
480 case 'ObjectField':
481 case 'Directive':
482 case 'NamedType':
483 case 'ScalarTypeDefinition':
484 case 'ObjectTypeDefinition':
485 case 'FieldDefinition':
486 case 'InputValueDefinition':
487 case 'InterfaceTypeDefinition':
488 case 'UnionTypeDefinition':
489 case 'EnumTypeDefinition':
490 case 'EnumValueDefinition':
491 case 'InputObjectTypeDefinition':
492 case 'DirectiveDefinition': {
493 return getName(node.name);
494 }
495 case 'Name': {
496 return node.value;
497 }
498 case 'Field': {
499 return getName(node.alias || node.name);
500 }
501 case 'VariableDefinition': {
502 return getName(node.variable);
503 }
504 }
505 return undefined;
506}
507function convertFactory(config) {
508 function resolveConventionName(type) {
509 if (!config.namingConvention) {
510 return (str, opts = {}) => {
511 return convertNameParts(str, pascalCase, getConfigValue((opts || {}).transformUnderscore, false));
512 };
513 }
514 if (typeof config.namingConvention === 'string') {
515 if (config.namingConvention === 'keep') {
516 return str => str;
517 }
518 return (str, opts = {}) => {
519 return convertNameParts(str, resolveExternalModuleAndFn(config.namingConvention), getConfigValue((opts || {}).transformUnderscore, false));
520 };
521 }
522 if (typeof config.namingConvention === 'function') {
523 return (str, opts = {}) => {
524 return convertNameParts(str, config.namingConvention, getConfigValue((opts || {}).transformUnderscore, false));
525 };
526 }
527 if (typeof config.namingConvention === 'object' && config.namingConvention[type] === 'keep') {
528 return str => str;
529 }
530 if (typeof config.namingConvention === 'object') {
531 if (!config.namingConvention[type]) {
532 return (str, opts = {}) => {
533 const transformUnderscore = config.namingConvention.transformUnderscore || (opts || {}).transformUnderscore;
534 return convertNameParts(str, pascalCase, getConfigValue(transformUnderscore, false));
535 };
536 }
537 return (str, opts = {}) => {
538 return convertNameParts(str, resolveExternalModuleAndFn(config.namingConvention[type]), getConfigValue((opts || {}).transformUnderscore, true));
539 };
540 }
541 return config.namingConvention[type];
542 }
543 return (node, opts) => {
544 const prefix = opts && opts.prefix;
545 const suffix = opts && opts.suffix;
546 const kind = getKind(node);
547 const str = [prefix || '', getName(node), suffix || ''].join('');
548 return resolveConventionName(kind)(str, opts);
549 };
550}
551
552function parseEnumValues(schema, mapOrStr = {}) {
553 const allTypes = schema.getTypeMap();
554 const allEnums = Object.keys(allTypes).filter(t => isEnumType(allTypes[t]));
555 if (typeof mapOrStr === 'object') {
556 for (const enumTypeName of allEnums) {
557 const enumType = schema.getType(enumTypeName);
558 for (const { name, value } of enumType.getValues()) {
559 if (value && value !== name) {
560 mapOrStr[enumTypeName] = mapOrStr[enumTypeName] || {};
561 if (typeof mapOrStr[enumTypeName] !== 'string' && !mapOrStr[enumTypeName][name]) {
562 mapOrStr[enumTypeName][name] = value;
563 }
564 }
565 }
566 }
567 const invalidMappings = Object.keys(mapOrStr).filter(gqlName => !allEnums.includes(gqlName));
568 if (invalidMappings.length > 0) {
569 throw new DetailedError(`Invalid 'enumValues' mapping!`, `The following types does not exist in your GraphQL schema: ${invalidMappings.join(', ')}`);
570 }
571 return Object.keys(mapOrStr).reduce((prev, gqlIdentifier) => {
572 const pointer = mapOrStr[gqlIdentifier];
573 if (typeof pointer === 'string') {
574 const mapper = parseMapper(pointer, gqlIdentifier);
575 return {
576 ...prev,
577 [gqlIdentifier]: {
578 isDefault: mapper.isExternal && mapper.default,
579 typeIdentifier: gqlIdentifier,
580 sourceFile: mapper.isExternal ? mapper.source : null,
581 sourceIdentifier: mapper.type,
582 importIdentifier: mapper.isExternal ? mapper.import : null,
583 mappedValues: null,
584 },
585 };
586 }
587 else if (typeof pointer === 'object') {
588 return {
589 ...prev,
590 [gqlIdentifier]: {
591 isDefault: false,
592 typeIdentifier: gqlIdentifier,
593 sourceFile: null,
594 sourceIdentifier: null,
595 importIdentifier: null,
596 mappedValues: pointer,
597 },
598 };
599 }
600 else {
601 throw new DetailedError(`Invalid "enumValues" configuration`, `Enum "${gqlIdentifier}": expected string or object (with enum values mapping)`);
602 }
603 }, {});
604 }
605 else if (typeof mapOrStr === 'string') {
606 return allEnums
607 .filter(enumName => !enumName.startsWith('__'))
608 .reduce((prev, enumName) => {
609 return {
610 ...prev,
611 [enumName]: {
612 isDefault: false,
613 typeIdentifier: enumName,
614 sourceFile: mapOrStr,
615 sourceIdentifier: enumName,
616 importIdentifier: enumName,
617 mappedValues: null,
618 },
619 };
620 }, {});
621 }
622 return {};
623}
624
625const DEFAULT_DECLARATION_KINDS = {
626 scalar: 'type',
627 input: 'type',
628 type: 'type',
629 interface: 'type',
630 arguments: 'type',
631};
632function normalizeDeclarationKind(declarationKind) {
633 if (typeof declarationKind === 'string') {
634 return {
635 scalar: declarationKind,
636 input: declarationKind,
637 type: declarationKind,
638 interface: declarationKind,
639 arguments: declarationKind,
640 };
641 }
642 return {
643 ...DEFAULT_DECLARATION_KINDS,
644 ...declarationKind,
645 };
646}
647
648const DEFAULT_AVOID_OPTIONALS = {
649 object: false,
650 inputValue: false,
651 field: false,
652};
653function normalizeAvoidOptionals(avoidOptionals) {
654 if (typeof avoidOptionals === 'boolean') {
655 return {
656 object: avoidOptionals,
657 inputValue: avoidOptionals,
658 field: avoidOptionals,
659 };
660 }
661 return {
662 ...DEFAULT_AVOID_OPTIONALS,
663 ...avoidOptionals,
664 };
665}
666
667function generateFragmentImportStatement(statement, kind) {
668 const { importSource: fragmentImportSource, ...rest } = statement;
669 const { identifiers, path, namespace } = fragmentImportSource;
670 const importSource = {
671 identifiers: identifiers
672 .filter(fragmentImport => kind === 'both' || kind === fragmentImport.kind)
673 .map(({ name }) => name),
674 path,
675 namespace,
676 };
677 return generateImportStatement({
678 importSource,
679 ...rest,
680 typesImport: kind === 'type' ? statement.typesImport : false,
681 });
682}
683function generateImportStatement(statement) {
684 const { baseDir, importSource, outputPath, typesImport } = statement;
685 const importPath = resolveImportPath(baseDir, outputPath, importSource.path);
686 const importNames = importSource.identifiers && importSource.identifiers.length
687 ? `{ ${Array.from(new Set(importSource.identifiers)).join(', ')} }`
688 : '*';
689 const importAlias = importSource.namespace ? ` as ${importSource.namespace}` : '';
690 const importStatement = typesImport ? 'import type' : 'import';
691 return `${importStatement} ${importNames}${importAlias} from '${importPath}';${importAlias ? '\n' : ''}`;
692}
693function resolveImportPath(baseDir, outputPath, sourcePath) {
694 const shouldAbsolute = !sourcePath.startsWith('~');
695 if (shouldAbsolute) {
696 const absGeneratedFilePath = resolve(baseDir, outputPath);
697 const absImportFilePath = resolve(baseDir, sourcePath);
698 return resolveRelativeImport(absGeneratedFilePath, absImportFilePath);
699 }
700 else {
701 return sourcePath.replace(`~`, '');
702 }
703}
704function resolveRelativeImport(from, to) {
705 if (!isAbsolute(from)) {
706 throw new Error(`Argument 'from' must be an absolute path, '${from}' given.`);
707 }
708 if (!isAbsolute(to)) {
709 throw new Error(`Argument 'to' must be an absolute path, '${to}' given.`);
710 }
711 return fixLocalFilePath(clearExtension(relative(dirname(from), to)));
712}
713function resolveImportSource(source) {
714 return typeof source === 'string' ? { path: source } : source;
715}
716function clearExtension(path) {
717 const parsedPath = parse(path);
718 return join(parsedPath.dir, parsedPath.name).replace(/\\/g, '/');
719}
720function fixLocalFilePath(path) {
721 return !path.startsWith('..') ? `./${path}` : path;
722}
723
724class BaseVisitor {
725 constructor(rawConfig, additionalConfig) {
726 this._declarationBlockConfig = {};
727 this._parsedConfig = {
728 convert: convertFactory(rawConfig),
729 typesPrefix: rawConfig.typesPrefix || '',
730 externalFragments: rawConfig.externalFragments || [],
731 fragmentImports: rawConfig.fragmentImports || [],
732 addTypename: !rawConfig.skipTypename,
733 nonOptionalTypename: !!rawConfig.nonOptionalTypename,
734 useTypeImports: !!rawConfig.useTypeImports,
735 ...(additionalConfig || {}),
736 };
737 this.scalars = {};
738 Object.keys(this.config.scalars || {}).forEach(key => {
739 this.scalars[key] = this.config.scalars[key].type;
740 });
741 autoBind(this);
742 }
743 getVisitorKindContextFromAncestors(ancestors) {
744 if (!ancestors) {
745 return [];
746 }
747 return ancestors.map(t => t.kind).filter(Boolean);
748 }
749 get config() {
750 return this._parsedConfig;
751 }
752 convertName(node, options) {
753 const useTypesPrefix = typeof (options && options.useTypesPrefix) === 'boolean' ? options.useTypesPrefix : true;
754 return (useTypesPrefix ? this.config.typesPrefix : '') + this.config.convert(node, options);
755 }
756 getOperationSuffix(node, operationType) {
757 const { omitOperationSuffix = false, dedupeOperationSuffix = false } = this.config;
758 const operationName = typeof node === 'string' ? node : node.name.value;
759 return omitOperationSuffix
760 ? ''
761 : dedupeOperationSuffix && operationName.toLowerCase().endsWith(operationType.toLowerCase())
762 ? ''
763 : operationType;
764 }
765 getFragmentSuffix(node) {
766 return this.getOperationSuffix(node, 'Fragment');
767 }
768 getFragmentName(node) {
769 return this.convertName(node, {
770 suffix: this.getFragmentSuffix(node),
771 useTypesPrefix: false,
772 });
773 }
774 getFragmentVariableName(node) {
775 const { omitOperationSuffix = false, dedupeOperationSuffix = false, fragmentVariableSuffix = 'FragmentDoc', fragmentVariablePrefix = '', } = this.config;
776 const fragmentName = typeof node === 'string' ? node : node.name.value;
777 const suffix = omitOperationSuffix
778 ? ''
779 : dedupeOperationSuffix &&
780 fragmentName.toLowerCase().endsWith('fragment') &&
781 fragmentVariableSuffix.toLowerCase().startsWith('fragment')
782 ? fragmentVariableSuffix.substring('fragment'.length)
783 : fragmentVariableSuffix;
784 return this.convertName(node, {
785 prefix: fragmentVariablePrefix,
786 suffix,
787 useTypesPrefix: false,
788 });
789 }
790 getPunctuation(declarationKind) {
791 return '';
792 }
793}
794
795class OperationVariablesToObject {
796 constructor(_scalars, _convertName, _namespacedImportName = null, _enumNames = [], _enumPrefix = true, _enumValues = {}) {
797 this._scalars = _scalars;
798 this._convertName = _convertName;
799 this._namespacedImportName = _namespacedImportName;
800 this._enumNames = _enumNames;
801 this._enumPrefix = _enumPrefix;
802 this._enumValues = _enumValues;
803 autoBind(this);
804 }
805 getName(node) {
806 if (node.name) {
807 if (typeof node.name === 'string') {
808 return node.name;
809 }
810 return node.name.value;
811 }
812 else if (node.variable) {
813 return node.variable.name.value;
814 }
815 return null;
816 }
817 transform(variablesNode) {
818 if (!variablesNode || variablesNode.length === 0) {
819 return null;
820 }
821 return (variablesNode.map(variable => indent(this.transformVariable(variable))).join(`${this.getPunctuation()}\n`) +
822 this.getPunctuation());
823 }
824 getScalar(name) {
825 const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
826 return `${prefix}Scalars['${name}']`;
827 }
828 transformVariable(variable) {
829 let typeValue = null;
830 const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
831 if (typeof variable.type === 'string') {
832 typeValue = variable.type;
833 }
834 else {
835 const baseType = getBaseTypeNode(variable.type);
836 const typeName = baseType.name.value;
837 if (this._scalars[typeName]) {
838 typeValue = this.getScalar(typeName);
839 }
840 else if (this._enumValues[typeName] && this._enumValues[typeName].sourceFile) {
841 typeValue = this._enumValues[typeName].typeIdentifier || this._enumValues[typeName].sourceIdentifier;
842 }
843 else {
844 typeValue = `${prefix}${this._convertName(baseType, {
845 useTypesPrefix: this._enumNames.includes(typeName) ? this._enumPrefix : true,
846 })}`;
847 }
848 }
849 const fieldName = this.getName(variable);
850 const fieldType = this.wrapAstTypeWithModifiers(typeValue, variable.type);
851 const hasDefaultValue = variable.defaultValue != null && typeof variable.defaultValue !== 'undefined';
852 const isNonNullType = variable.type.kind === Kind.NON_NULL_TYPE;
853 const formattedFieldString = this.formatFieldString(fieldName, isNonNullType, hasDefaultValue);
854 const formattedTypeString = this.formatTypeString(fieldType, isNonNullType, hasDefaultValue);
855 return `${formattedFieldString}: ${formattedTypeString}`;
856 }
857 wrapAstTypeWithModifiers(baseType, typeNode) {
858 throw new Error(`You must override "wrapAstTypeWithModifiers" of OperationVariablesToObject!`);
859 }
860 formatFieldString(fieldName, isNonNullType, hasDefaultValue) {
861 return fieldName;
862 }
863 formatTypeString(fieldType, isNonNullType, hasDefaultValue) {
864 const prefix = this._namespacedImportName ? `${this._namespacedImportName}.` : '';
865 if (hasDefaultValue) {
866 return `${prefix}Maybe<${fieldType}>`;
867 }
868 return fieldType;
869 }
870 getPunctuation() {
871 return ',';
872 }
873}
874
875class BaseTypesVisitor extends BaseVisitor {
876 constructor(_schema, rawConfig, additionalConfig, defaultScalars = DEFAULT_SCALARS) {
877 super(rawConfig, {
878 enumPrefix: getConfigValue(rawConfig.enumPrefix, true),
879 onlyOperationTypes: getConfigValue(rawConfig.onlyOperationTypes, false),
880 addUnderscoreToArgsType: getConfigValue(rawConfig.addUnderscoreToArgsType, false),
881 enumValues: parseEnumValues(_schema, rawConfig.enumValues),
882 declarationKind: normalizeDeclarationKind(rawConfig.declarationKind),
883 scalars: buildScalars(_schema, rawConfig.scalars, defaultScalars),
884 fieldWrapperValue: getConfigValue(rawConfig.fieldWrapperValue, 'T'),
885 wrapFieldDefinitions: getConfigValue(rawConfig.wrapFieldDefinitions, false),
886 ...additionalConfig,
887 });
888 this._schema = _schema;
889 this._argumentsTransformer = new OperationVariablesToObject(this.scalars, this.convertName);
890 }
891 getExportPrefix() {
892 return 'export ';
893 }
894 getFieldWrapperValue() {
895 if (this.config.fieldWrapperValue) {
896 return `${this.getExportPrefix()}type FieldWrapper<T> = ${this.config.fieldWrapperValue};`;
897 }
898 return '';
899 }
900 getScalarsImports() {
901 return Object.keys(this.config.scalars)
902 .map(enumName => {
903 const mappedValue = this.config.scalars[enumName];
904 if (mappedValue.isExternal) {
905 return this._buildTypeImport(mappedValue.import, mappedValue.source, mappedValue.default);
906 }
907 return null;
908 })
909 .filter(a => a);
910 }
911 get scalarsDefinition() {
912 const allScalars = Object.keys(this.config.scalars).map(scalarName => {
913 const scalarValue = this.config.scalars[scalarName].type;
914 const scalarType = this._schema.getType(scalarName);
915 const comment = scalarType && scalarType.astNode && scalarType.description ? transformComment(scalarType.description, 1) : '';
916 const { scalar } = this._parsedConfig.declarationKind;
917 return comment + indent(`${scalarName}: ${scalarValue}${this.getPunctuation(scalar)}`);
918 });
919 return new DeclarationBlock(this._declarationBlockConfig)
920 .export()
921 .asKind(this._parsedConfig.declarationKind.scalar)
922 .withName('Scalars')
923 .withComment('All built-in and custom scalars, mapped to their actual values')
924 .withBlock(allScalars.join('\n')).string;
925 }
926 setDeclarationBlockConfig(config) {
927 this._declarationBlockConfig = config;
928 }
929 setArgumentsTransformer(argumentsTransfomer) {
930 this._argumentsTransformer = argumentsTransfomer;
931 }
932 NonNullType(node) {
933 const asString = node.type;
934 return asString;
935 }
936 getInputObjectDeclarationBlock(node) {
937 return new DeclarationBlock(this._declarationBlockConfig)
938 .export()
939 .asKind(this._parsedConfig.declarationKind.input)
940 .withName(this.convertName(node))
941 .withComment(node.description)
942 .withBlock(node.fields.join('\n'));
943 }
944 InputObjectTypeDefinition(node) {
945 return this.getInputObjectDeclarationBlock(node).string;
946 }
947 InputValueDefinition(node) {
948 const comment = transformComment(node.description, 1);
949 const { input } = this._parsedConfig.declarationKind;
950 return comment + indent(`${node.name}: ${node.type}${this.getPunctuation(input)}`);
951 }
952 Name(node) {
953 return node.value;
954 }
955 FieldDefinition(node) {
956 const typeString = node.type;
957 const { type } = this._parsedConfig.declarationKind;
958 const comment = this.getFieldComment(node);
959 return comment + indent(`${node.name}: ${typeString}${this.getPunctuation(type)}`);
960 }
961 UnionTypeDefinition(node, key, parent) {
962 if (this.config.onlyOperationTypes)
963 return '';
964 const originalNode = parent[key];
965 const possibleTypes = originalNode.types
966 .map(t => (this.scalars[t.name.value] ? this._getScalar(t.name.value) : this.convertName(t)))
967 .join(' | ');
968 return new DeclarationBlock(this._declarationBlockConfig)
969 .export()
970 .asKind('type')
971 .withName(this.convertName(node))
972 .withComment(node.description)
973 .withContent(possibleTypes).string;
974 }
975 mergeInterfaces(interfaces, hasOtherFields) {
976 return interfaces.join(' & ') + (interfaces.length && hasOtherFields ? ' & ' : '');
977 }
978 appendInterfacesAndFieldsToBlock(block, interfaces, fields) {
979 block.withContent(this.mergeInterfaces(interfaces, fields.length > 0));
980 block.withBlock(this.mergeAllFields(fields, interfaces.length > 0));
981 }
982 getObjectTypeDeclarationBlock(node, originalNode) {
983 const optionalTypename = this.config.nonOptionalTypename ? '__typename' : '__typename?';
984 const { type } = this._parsedConfig.declarationKind;
985 const allFields = [
986 ...(this.config.addTypename
987 ? [
988 indent(`${this.config.immutableTypes ? 'readonly ' : ''}${optionalTypename}: '${node.name}'${this.getPunctuation(type)}`),
989 ]
990 : []),
991 ...node.fields,
992 ];
993 const interfacesNames = originalNode.interfaces ? originalNode.interfaces.map(i => this.convertName(i)) : [];
994 const declarationBlock = new DeclarationBlock(this._declarationBlockConfig)
995 .export()
996 .asKind(type)
997 .withName(this.convertName(node))
998 .withComment(node.description);
999 if (type === 'interface' || type === 'class') {
1000 if (interfacesNames.length > 0) {
1001 declarationBlock.withContent('extends ' + interfacesNames.join(', ') + (allFields.length > 0 ? ' ' : ' {}'));
1002 }
1003 declarationBlock.withBlock(this.mergeAllFields(allFields, false));
1004 }
1005 else {
1006 this.appendInterfacesAndFieldsToBlock(declarationBlock, interfacesNames, allFields);
1007 }
1008 return declarationBlock;
1009 }
1010 getFieldComment(node) {
1011 let commentText = node.description;
1012 const deprecationDirective = node.directives.find((v) => v.name === 'deprecated');
1013 if (deprecationDirective) {
1014 const deprecationReason = this.getDeprecationReason(deprecationDirective);
1015 commentText = `${commentText ? `${commentText}\n` : ''}@deprecated ${deprecationReason}`;
1016 }
1017 const comment = transformComment(commentText, 1);
1018 return comment;
1019 }
1020 mergeAllFields(allFields, hasInterfaces) {
1021 return allFields.join('\n');
1022 }
1023 ObjectTypeDefinition(node, key, parent) {
1024 if (this.config.onlyOperationTypes)
1025 return '';
1026 const originalNode = parent[key];
1027 return [this.getObjectTypeDeclarationBlock(node, originalNode).string, this.buildArgumentsBlock(originalNode)]
1028 .filter(f => f)
1029 .join('\n\n');
1030 }
1031 getInterfaceTypeDeclarationBlock(node, originalNode) {
1032 const declarationBlock = new DeclarationBlock(this._declarationBlockConfig)
1033 .export()
1034 .asKind(this._parsedConfig.declarationKind.interface)
1035 .withName(this.convertName(node))
1036 .withComment(node.description);
1037 return declarationBlock.withBlock(node.fields.join('\n'));
1038 }
1039 InterfaceTypeDefinition(node, key, parent) {
1040 if (this.config.onlyOperationTypes)
1041 return '';
1042 const originalNode = parent[key];
1043 return [this.getInterfaceTypeDeclarationBlock(node, originalNode).string, this.buildArgumentsBlock(originalNode)]
1044 .filter(f => f)
1045 .join('\n\n');
1046 }
1047 ScalarTypeDefinition(node) {
1048 // We empty this because we handle scalars in a different way, see constructor.
1049 return '';
1050 }
1051 _buildTypeImport(identifier, source, asDefault = false) {
1052 const { useTypeImports } = this.config;
1053 if (asDefault) {
1054 if (useTypeImports) {
1055 return `import type { default as ${identifier} } from '${source}';`;
1056 }
1057 return `import ${identifier} from '${source}';`;
1058 }
1059 return `import${useTypeImports ? ' type' : ''} { ${identifier} } from '${source}';`;
1060 }
1061 handleEnumValueMapper(typeIdentifier, importIdentifier, sourceIdentifier, sourceFile) {
1062 const importStatement = this._buildTypeImport(importIdentifier || sourceIdentifier, sourceFile);
1063 if (importIdentifier !== sourceIdentifier || sourceIdentifier !== typeIdentifier) {
1064 return [importStatement, `import ${typeIdentifier} = ${sourceIdentifier};`];
1065 }
1066 return [importStatement];
1067 }
1068 getEnumsImports() {
1069 return flatMap(Object.keys(this.config.enumValues), enumName => {
1070 const mappedValue = this.config.enumValues[enumName];
1071 if (mappedValue.sourceFile) {
1072 if (mappedValue.isDefault) {
1073 return [this._buildTypeImport(mappedValue.typeIdentifier, mappedValue.sourceFile, true)];
1074 }
1075 return this.handleEnumValueMapper(mappedValue.typeIdentifier, mappedValue.importIdentifier, mappedValue.sourceIdentifier, mappedValue.sourceFile);
1076 }
1077 return [];
1078 }).filter(a => a);
1079 }
1080 EnumTypeDefinition(node) {
1081 const enumName = node.name;
1082 // In case of mapped external enum string
1083 if (this.config.enumValues[enumName] && this.config.enumValues[enumName].sourceFile) {
1084 return null;
1085 }
1086 return new DeclarationBlock(this._declarationBlockConfig)
1087 .export()
1088 .asKind('enum')
1089 .withName(this.convertName(node, { useTypesPrefix: this.config.enumPrefix }))
1090 .withComment(node.description)
1091 .withBlock(this.buildEnumValuesBlock(enumName, node.values)).string;
1092 }
1093 // We are using it in order to transform "description" field
1094 StringValue(node) {
1095 return node.value;
1096 }
1097 buildEnumValuesBlock(typeName, values) {
1098 return values
1099 .map(enumOption => {
1100 const optionName = this.convertName(enumOption, { useTypesPrefix: false, transformUnderscore: true });
1101 const comment = transformComment(enumOption.description, 1);
1102 let enumValue = enumOption.name;
1103 if (this.config.enumValues[typeName] &&
1104 this.config.enumValues[typeName].mappedValues &&
1105 typeof this.config.enumValues[typeName].mappedValues[enumValue] !== 'undefined') {
1106 enumValue = this.config.enumValues[typeName].mappedValues[enumValue];
1107 }
1108 return (comment +
1109 indent(`${optionName}${this._declarationBlockConfig.enumNameValueSeparator} ${wrapWithSingleQuotes(enumValue)}`));
1110 })
1111 .join(',\n');
1112 }
1113 DirectiveDefinition(node) {
1114 return '';
1115 }
1116 getArgumentsObjectDeclarationBlock(node, name, field) {
1117 return new DeclarationBlock(this._declarationBlockConfig)
1118 .export()
1119 .asKind(this._parsedConfig.declarationKind.arguments)
1120 .withName(this.convertName(name))
1121 .withComment(node.description)
1122 .withBlock(this._argumentsTransformer.transform(field.arguments));
1123 }
1124 getArgumentsObjectTypeDefinition(node, name, field) {
1125 return this.getArgumentsObjectDeclarationBlock(node, name, field).string;
1126 }
1127 buildArgumentsBlock(node) {
1128 const fieldsWithArguments = node.fields.filter(field => field.arguments && field.arguments.length > 0) || [];
1129 return fieldsWithArguments
1130 .map(field => {
1131 const name = node.name.value +
1132 (this.config.addUnderscoreToArgsType ? '_' : '') +
1133 this.convertName(field, {
1134 useTypesPrefix: false,
1135 }) +
1136 'Args';
1137 return this.getArgumentsObjectTypeDefinition(node, name, field);
1138 })
1139 .join('\n\n');
1140 }
1141 _getScalar(name) {
1142 return `Scalars['${name}']`;
1143 }
1144 _getTypeForNode(node) {
1145 const typeAsString = node.name;
1146 if (this.scalars[typeAsString]) {
1147 return this._getScalar(typeAsString);
1148 }
1149 else if (this.config.enumValues[typeAsString]) {
1150 return this.config.enumValues[typeAsString].typeIdentifier;
1151 }
1152 const schemaType = this._schema.getType(node.name);
1153 if (schemaType && isEnumType(schemaType)) {
1154 return this.convertName(node, { useTypesPrefix: this.config.enumPrefix });
1155 }
1156 return this.convertName(node);
1157 }
1158 NamedType(node, key, parent, path, ancestors) {
1159 const currentVisitContext = this.getVisitorKindContextFromAncestors(ancestors);
1160 const isVisitingInputType = currentVisitContext.includes(Kind.INPUT_OBJECT_TYPE_DEFINITION);
1161 const typeToUse = this._getTypeForNode(node);
1162 if (!isVisitingInputType && this.config.fieldWrapperValue && this.config.wrapFieldDefinitions) {
1163 return `FieldWrapper<${typeToUse}>`;
1164 }
1165 return typeToUse;
1166 }
1167 ListType(node) {
1168 const asString = node.type;
1169 return this.wrapWithListType(asString);
1170 }
1171 SchemaDefinition() {
1172 return null;
1173 }
1174 getDeprecationReason(directive) {
1175 if (directive.name === 'deprecated') {
1176 const hasArguments = directive.arguments.length > 0;
1177 let reason = 'Field no longer supported';
1178 if (hasArguments) {
1179 reason = directive.arguments[0].value;
1180 }
1181 return reason;
1182 }
1183 }
1184 wrapWithListType(str) {
1185 return `Array<${str}>`;
1186 }
1187}
1188
1189function getRootType(operation, schema) {
1190 switch (operation) {
1191 case 'query':
1192 return schema.getQueryType();
1193 case 'mutation':
1194 return schema.getMutationType();
1195 case 'subscription':
1196 return schema.getSubscriptionType();
1197 }
1198}
1199class BaseDocumentsVisitor extends BaseVisitor {
1200 constructor(rawConfig, additionalConfig, _schema, defaultScalars = DEFAULT_SCALARS) {
1201 super(rawConfig, {
1202 exportFragmentSpreadSubTypes: getConfigValue(rawConfig.exportFragmentSpreadSubTypes, false),
1203 enumPrefix: getConfigValue(rawConfig.enumPrefix, true),
1204 preResolveTypes: getConfigValue(rawConfig.preResolveTypes, false),
1205 dedupeOperationSuffix: getConfigValue(rawConfig.dedupeOperationSuffix, false),
1206 omitOperationSuffix: getConfigValue(rawConfig.omitOperationSuffix, false),
1207 skipTypeNameForRoot: getConfigValue(rawConfig.skipTypeNameForRoot, false),
1208 namespacedImportName: getConfigValue(rawConfig.namespacedImportName, null),
1209 addTypename: !rawConfig.skipTypename,
1210 globalNamespace: !!rawConfig.globalNamespace,
1211 operationResultSuffix: getConfigValue(rawConfig.operationResultSuffix, ''),
1212 scalars: buildScalars(_schema, rawConfig.scalars, defaultScalars),
1213 ...(additionalConfig || {}),
1214 });
1215 this._schema = _schema;
1216 this._unnamedCounter = 1;
1217 this._globalDeclarations = new Set();
1218 autoBind(this);
1219 this._variablesTransfomer = new OperationVariablesToObject(this.scalars, this.convertName, this.config.namespacedImportName);
1220 }
1221 getGlobalDeclarations(noExport = false) {
1222 return Array.from(this._globalDeclarations).map(t => (noExport ? t : `export ${t}`));
1223 }
1224 setSelectionSetHandler(handler) {
1225 this._selectionSetToObject = handler;
1226 }
1227 setDeclarationBlockConfig(config) {
1228 this._declarationBlockConfig = config;
1229 }
1230 setVariablesTransformer(variablesTransfomer) {
1231 this._variablesTransfomer = variablesTransfomer;
1232 }
1233 get schema() {
1234 return this._schema;
1235 }
1236 get addTypename() {
1237 return this._parsedConfig.addTypename;
1238 }
1239 handleAnonymousOperation(node) {
1240 const name = node.name && node.name.value;
1241 if (name) {
1242 return this.convertName(node, {
1243 useTypesPrefix: false,
1244 });
1245 }
1246 return this.convertName(this._unnamedCounter++ + '', {
1247 prefix: 'Unnamed_',
1248 suffix: '_',
1249 useTypesPrefix: false,
1250 });
1251 }
1252 FragmentDefinition(node) {
1253 const fragmentRootType = this._schema.getType(node.typeCondition.name.value);
1254 const selectionSet = this._selectionSetToObject.createNext(fragmentRootType, node.selectionSet);
1255 const fragmentSuffix = this.getFragmentSuffix(node);
1256 return selectionSet.transformFragmentSelectionSetToTypes(node.name.value, fragmentSuffix, this._declarationBlockConfig);
1257 }
1258 applyVariablesWrapper(variablesBlock) {
1259 return variablesBlock;
1260 }
1261 OperationDefinition(node) {
1262 const name = this.handleAnonymousOperation(node);
1263 const operationRootType = getRootType(node.operation, this._schema);
1264 if (!operationRootType) {
1265 throw new Error(`Unable to find root schema type for operation type "${node.operation}"!`);
1266 }
1267 const selectionSet = this._selectionSetToObject.createNext(operationRootType, node.selectionSet);
1268 const visitedOperationVariables = this._variablesTransfomer.transform(node.variableDefinitions);
1269 const operationType = pascalCase(node.operation);
1270 const operationTypeSuffix = this.getOperationSuffix(name, operationType);
1271 const operationResult = new DeclarationBlock(this._declarationBlockConfig)
1272 .export()
1273 .asKind('type')
1274 .withName(this.convertName(name, {
1275 suffix: operationTypeSuffix + this._parsedConfig.operationResultSuffix,
1276 }))
1277 .withContent(selectionSet.transformSelectionSet()).string;
1278 const operationVariables = new DeclarationBlock({
1279 ...this._declarationBlockConfig,
1280 blockTransformer: t => this.applyVariablesWrapper(t),
1281 })
1282 .export()
1283 .asKind('type')
1284 .withName(this.convertName(name, {
1285 suffix: operationTypeSuffix + 'Variables',
1286 }))
1287 .withBlock(visitedOperationVariables).string;
1288 return [operationVariables, operationResult].filter(r => r).join('\n\n');
1289 }
1290}
1291
1292class BaseResolversVisitor extends BaseVisitor {
1293 constructor(rawConfig, additionalConfig, _schema, defaultScalars = DEFAULT_SCALARS) {
1294 super(rawConfig, {
1295 immutableTypes: getConfigValue(rawConfig.immutableTypes, false),
1296 optionalResolveType: getConfigValue(rawConfig.optionalResolveType, false),
1297 enumPrefix: getConfigValue(rawConfig.enumPrefix, true),
1298 federation: getConfigValue(rawConfig.federation, false),
1299 resolverTypeWrapperSignature: getConfigValue(rawConfig.resolverTypeWrapperSignature, 'Promise<T> | T'),
1300 enumValues: parseEnumValues(_schema, rawConfig.enumValues),
1301 addUnderscoreToArgsType: getConfigValue(rawConfig.addUnderscoreToArgsType, false),
1302 contextType: parseMapper(rawConfig.contextType || 'any', 'ContextType'),
1303 fieldContextTypes: getConfigValue(rawConfig.fieldContextTypes, []),
1304 resolverTypeSuffix: getConfigValue(rawConfig.resolverTypeSuffix, 'Resolvers'),
1305 allResolversTypeName: getConfigValue(rawConfig.allResolversTypeName, 'Resolvers'),
1306 rootValueType: parseMapper(rawConfig.rootValueType || '{}', 'RootValueType'),
1307 namespacedImportName: getConfigValue(rawConfig.namespacedImportName, ''),
1308 avoidOptionals: getConfigValue(rawConfig.avoidOptionals, false),
1309 defaultMapper: rawConfig.defaultMapper
1310 ? parseMapper(rawConfig.defaultMapper || 'any', 'DefaultMapperType')
1311 : null,
1312 mappers: transformMappers(rawConfig.mappers || {}, rawConfig.mapperTypeSuffix),
1313 scalars: buildScalars(_schema, rawConfig.scalars, defaultScalars),
1314 ...(additionalConfig || {}),
1315 });
1316 this._schema = _schema;
1317 this._declarationBlockConfig = {};
1318 this._collectedResolvers = {};
1319 this._collectedDirectiveResolvers = {};
1320 this._usedMappers = {};
1321 this._resolversTypes = {};
1322 this._resolversParentTypes = {};
1323 this._rootTypeNames = [];
1324 this._globalDeclarations = new Set();
1325 this._hasScalars = false;
1326 this._hasFederation = false;
1327 autoBind(this);
1328 this._federation = new ApolloFederation({ enabled: this.config.federation, schema: this.schema });
1329 this._rootTypeNames = getRootTypeNames(_schema);
1330 this._variablesTransfomer = new OperationVariablesToObject(this.scalars, this.convertName, this.config.namespacedImportName);
1331 this._resolversTypes = this.createResolversFields(type => this.applyResolverTypeWrapper(type), type => this.clearResolverTypeWrapper(type), name => this.getTypeToUse(name));
1332 this._resolversParentTypes = this.createResolversFields(type => type, type => type, name => this.getParentTypeToUse(name), namedType => !isEnumType(namedType));
1333 this._fieldContextTypeMap = this.createFieldContextTypeMap();
1334 }
1335 getResolverTypeWrapperSignature() {
1336 return `export type ResolverTypeWrapper<T> = ${this.config.resolverTypeWrapperSignature};`;
1337 }
1338 shouldMapType(type, checkedBefore = {}, duringCheck = []) {
1339 if (checkedBefore[type.name] !== undefined) {
1340 return checkedBefore[type.name];
1341 }
1342 if (type.name.startsWith('__') || this.config.scalars[type.name]) {
1343 return false;
1344 }
1345 if (this.config.mappers[type.name]) {
1346 return true;
1347 }
1348 if (isObjectType(type) || isInterfaceType(type)) {
1349 const fields = type.getFields();
1350 return Object.keys(fields)
1351 .filter(fieldName => {
1352 const field = fields[fieldName];
1353 const fieldType = getBaseType(field.type);
1354 return !duringCheck.includes(fieldType.name);
1355 })
1356 .some(fieldName => {
1357 const field = fields[fieldName];
1358 const fieldType = getBaseType(field.type);
1359 if (checkedBefore[fieldType.name] !== undefined) {
1360 return checkedBefore[fieldType.name];
1361 }
1362 if (this.config.mappers[type.name]) {
1363 return true;
1364 }
1365 duringCheck.push(type.name);
1366 const innerResult = this.shouldMapType(fieldType, checkedBefore, duringCheck);
1367 return innerResult;
1368 });
1369 }
1370 return false;
1371 }
1372 convertName(node, options, applyNamespacedImport = false) {
1373 const sourceType = super.convertName(node, options);
1374 return `${applyNamespacedImport && this.config.namespacedImportName ? this.config.namespacedImportName + '.' : ''}${sourceType}`;
1375 }
1376 // Kamil: this one is heeeeavvyyyy
1377 createResolversFields(applyWrapper, clearWrapper, getTypeToUse, shouldInclude) {
1378 const allSchemaTypes = this._schema.getTypeMap();
1379 const nestedMapping = {};
1380 const typeNames = this._federation.filterTypeNames(Object.keys(allSchemaTypes));
1381 typeNames.forEach(typeName => {
1382 const schemaType = allSchemaTypes[typeName];
1383 nestedMapping[typeName] = this.shouldMapType(schemaType, nestedMapping);
1384 });
1385 return typeNames.reduce((prev, typeName) => {
1386 const schemaType = allSchemaTypes[typeName];
1387 if (typeName.startsWith('__') || (shouldInclude && !shouldInclude(schemaType))) {
1388 return prev;
1389 }
1390 let shouldApplyOmit = false;
1391 const isRootType = this._rootTypeNames.includes(typeName);
1392 const isMapped = this.config.mappers[typeName];
1393 const isScalar = this.config.scalars[typeName];
1394 const hasDefaultMapper = !!(this.config.defaultMapper && this.config.defaultMapper.type);
1395 if (isRootType) {
1396 prev[typeName] = applyWrapper(this.config.rootValueType.type);
1397 return prev;
1398 }
1399 else if (isInterfaceType(schemaType)) {
1400 const allTypesMap = this._schema.getTypeMap();
1401 const implementingTypes = [];
1402 for (const graphqlType of Object.values(allTypesMap)) {
1403 if (graphqlType instanceof GraphQLObjectType) {
1404 const allInterfaces = graphqlType.getInterfaces();
1405 if (allInterfaces.some(int => int.name === schemaType.name)) {
1406 implementingTypes.push(graphqlType.name);
1407 }
1408 }
1409 }
1410 const possibleTypes = implementingTypes.map(name => getTypeToUse(name)).join(' | ') || 'never';
1411 prev[typeName] = possibleTypes;
1412 return prev;
1413 }
1414 else if (isEnumType(schemaType) && this.config.enumValues[typeName]) {
1415 prev[typeName] =
1416 this.config.enumValues[typeName].sourceIdentifier ||
1417 this.convertName(this.config.enumValues[typeName].typeIdentifier);
1418 }
1419 else if (isMapped && this.config.mappers[typeName].type) {
1420 this.markMapperAsUsed(typeName);
1421 prev[typeName] = applyWrapper(this.config.mappers[typeName].type);
1422 }
1423 else if (hasDefaultMapper && !hasPlaceholder(this.config.defaultMapper.type)) {
1424 prev[typeName] = applyWrapper(this.config.defaultMapper.type);
1425 }
1426 else if (isScalar) {
1427 prev[typeName] = applyWrapper(this._getScalar(typeName));
1428 }
1429 else if (isUnionType(schemaType)) {
1430 prev[typeName] = schemaType
1431 .getTypes()
1432 .map(type => getTypeToUse(type.name))
1433 .join(' | ');
1434 }
1435 else {
1436 shouldApplyOmit = true;
1437 prev[typeName] = this.convertName(typeName, { useTypesPrefix: this.config.enumPrefix }, true);
1438 }
1439 if (shouldApplyOmit && prev[typeName] !== 'any' && isObjectType(schemaType)) {
1440 const fields = schemaType.getFields();
1441 const relevantFields = this._federation
1442 .filterFieldNames(Object.keys(fields))
1443 .map(fieldName => {
1444 const field = fields[fieldName];
1445 const baseType = getBaseType(field.type);
1446 const isUnion = isUnionType(baseType);
1447 if (!this.config.mappers[baseType.name] && !isUnion && !nestedMapping[baseType.name]) {
1448 return null;
1449 }
1450 const addOptionalSign = !this.config.avoidOptionals && !isNonNullType(field.type);
1451 return {
1452 addOptionalSign,
1453 fieldName,
1454 replaceWithType: wrapTypeWithModifiers(getTypeToUse(baseType.name), field.type, {
1455 wrapOptional: this.applyMaybe,
1456 wrapArray: this.wrapWithArray,
1457 }),
1458 };
1459 })
1460 .filter(a => a);
1461 if (relevantFields.length > 0) {
1462 // Puts ResolverTypeWrapper on top of an entire type
1463 prev[typeName] = applyWrapper(this.replaceFieldsInType(prev[typeName], relevantFields));
1464 }
1465 else {
1466 // We still want to use ResolverTypeWrapper, even if we don't touch any fields
1467 prev[typeName] = applyWrapper(prev[typeName]);
1468 }
1469 }
1470 if (isMapped && hasPlaceholder(prev[typeName])) {
1471 prev[typeName] = replacePlaceholder(prev[typeName], typeName);
1472 }
1473 if (!isMapped && hasDefaultMapper && hasPlaceholder(this.config.defaultMapper.type)) {
1474 // Make sure the inner type has no ResolverTypeWrapper
1475 const name = clearWrapper(isScalar ? this._getScalar(typeName) : prev[typeName]);
1476 const replaced = replacePlaceholder(this.config.defaultMapper.type, name);
1477 // Don't wrap Union with ResolverTypeWrapper, each inner type already has it
1478 if (isUnionType(schemaType)) {
1479 prev[typeName] = replaced;
1480 }
1481 else {
1482 prev[typeName] = applyWrapper(replacePlaceholder(this.config.defaultMapper.type, name));
1483 }
1484 }
1485 return prev;
1486 }, {});
1487 }
1488 replaceFieldsInType(typeName, relevantFields) {
1489 this._globalDeclarations.add(OMIT_TYPE);
1490 return `Omit<${typeName}, ${relevantFields.map(f => `'${f.fieldName}'`).join(' | ')}> & { ${relevantFields
1491 .map(f => `${f.fieldName}${f.addOptionalSign ? '?' : ''}: ${f.replaceWithType}`)
1492 .join(', ')} }`;
1493 }
1494 applyMaybe(str) {
1495 return `Maybe<${str}>`;
1496 }
1497 applyResolverTypeWrapper(str) {
1498 return `ResolverTypeWrapper<${this.clearResolverTypeWrapper(str)}>`;
1499 }
1500 clearMaybe(str) {
1501 if (str.startsWith('Maybe<')) {
1502 return str.replace(/Maybe<(.*?)>$/, '$1');
1503 }
1504 return str;
1505 }
1506 clearResolverTypeWrapper(str) {
1507 if (str.startsWith('ResolverTypeWrapper<')) {
1508 return str.replace(/ResolverTypeWrapper<(.*?)>$/, '$1');
1509 }
1510 return str;
1511 }
1512 wrapWithArray(t) {
1513 if (this.config.immutableTypes) {
1514 return `ReadonlyArray<${t}>`;
1515 }
1516 return `Array<${t}>`;
1517 }
1518 createFieldContextTypeMap() {
1519 return this.config.fieldContextTypes.reduce((prev, fieldContextType) => {
1520 const items = fieldContextType.split('#');
1521 if (items.length === 3) {
1522 const [path, source, contextTypeName] = items;
1523 return { ...prev, [path]: parseMapper(`${source}#${contextTypeName}`) };
1524 }
1525 const [path, contextType] = items;
1526 return { ...prev, [path]: parseMapper(contextType) };
1527 }, {});
1528 }
1529 buildResolversTypes() {
1530 const declarationKind = 'type';
1531 return new DeclarationBlock(this._declarationBlockConfig)
1532 .export()
1533 .asKind(declarationKind)
1534 .withName(this.convertName('ResolversTypes'))
1535 .withComment('Mapping between all available schema types and the resolvers types')
1536 .withBlock(Object.keys(this._resolversTypes)
1537 .map(typeName => indent(`${typeName}: ${this._resolversTypes[typeName]}${this.getPunctuation(declarationKind)}`))
1538 .join('\n')).string;
1539 }
1540 buildResolversParentTypes() {
1541 const declarationKind = 'type';
1542 return new DeclarationBlock(this._declarationBlockConfig)
1543 .export()
1544 .asKind(declarationKind)
1545 .withName(this.convertName('ResolversParentTypes'))
1546 .withComment('Mapping between all available schema types and the resolvers parents')
1547 .withBlock(Object.keys(this._resolversParentTypes)
1548 .map(typeName => indent(`${typeName}: ${this._resolversParentTypes[typeName]}${this.getPunctuation(declarationKind)}`))
1549 .join('\n')).string;
1550 }
1551 get schema() {
1552 return this._schema;
1553 }
1554 get defaultMapperType() {
1555 return this.config.defaultMapper.type;
1556 }
1557 get unusedMappers() {
1558 return Object.keys(this.config.mappers).filter(name => !this._usedMappers[name]);
1559 }
1560 get globalDeclarations() {
1561 return Array.from(this._globalDeclarations);
1562 }
1563 isMapperImported(groupedMappers, identifier, source) {
1564 const exists = !groupedMappers[source] ? false : !!groupedMappers[source].find(m => m.identifier === identifier);
1565 const existsFromEnums = !!Object.keys(this.config.enumValues)
1566 .map(key => this.config.enumValues[key])
1567 .find(o => o.sourceFile === source && o.typeIdentifier === identifier);
1568 return exists || existsFromEnums;
1569 }
1570 get mappersImports() {
1571 const groupedMappers = {};
1572 const addMapper = (source, identifier, asDefault) => {
1573 if (!this.isMapperImported(groupedMappers, identifier, source)) {
1574 if (!groupedMappers[source]) {
1575 groupedMappers[source] = [];
1576 }
1577 groupedMappers[source].push({ identifier, asDefault });
1578 }
1579 };
1580 Object.keys(this.config.mappers)
1581 .map(gqlTypeName => ({ gqlType: gqlTypeName, mapper: this.config.mappers[gqlTypeName] }))
1582 .filter(({ mapper }) => mapper.isExternal)
1583 .forEach(({ mapper }) => {
1584 const externalMapper = mapper;
1585 const identifier = stripMapperTypeInterpolation(externalMapper.import);
1586 addMapper(externalMapper.source, identifier, externalMapper.default);
1587 });
1588 if (this.config.contextType.isExternal) {
1589 addMapper(this.config.contextType.source, this.config.contextType.import, this.config.contextType.default);
1590 }
1591 if (this.config.rootValueType.isExternal) {
1592 addMapper(this.config.rootValueType.source, this.config.rootValueType.import, this.config.rootValueType.default);
1593 }
1594 if (this.config.defaultMapper && this.config.defaultMapper.isExternal) {
1595 const identifier = stripMapperTypeInterpolation(this.config.defaultMapper.import);
1596 addMapper(this.config.defaultMapper.source, identifier, this.config.defaultMapper.default);
1597 }
1598 Object.values(this._fieldContextTypeMap).forEach(parsedMapper => {
1599 if (parsedMapper.isExternal) {
1600 addMapper(parsedMapper.source, parsedMapper.import, parsedMapper.default);
1601 }
1602 });
1603 return Object.keys(groupedMappers)
1604 .map(source => this.buildMapperImport(source, groupedMappers[source]))
1605 .filter(Boolean);
1606 }
1607 buildMapperImport(source, types) {
1608 if (!types || types.length === 0) {
1609 return null;
1610 }
1611 const defaultType = types.find(t => t.asDefault === true);
1612 let namedTypes = types.filter(t => !t.asDefault);
1613 if (this.config.useTypeImports) {
1614 if (defaultType) {
1615 // default as Baz
1616 namedTypes = [{ identifier: `default as ${defaultType.identifier}` }, ...namedTypes];
1617 }
1618 // { Foo, Bar as BarModel }
1619 const namedImports = namedTypes.length ? `{ ${namedTypes.map(t => t.identifier).join(', ')} }` : '';
1620 // { default as Baz, Foo, Bar as BarModel }
1621 return `import type ${[namedImports].filter(Boolean).join(', ')} from '${source}';`;
1622 }
1623 // { Foo, Bar as BarModel }
1624 const namedImports = namedTypes.length ? `{ ${namedTypes.map(t => t.identifier).join(', ')} }` : '';
1625 // Baz
1626 const defaultImport = defaultType ? defaultType.identifier : '';
1627 // Baz, { Foo, Bar as BarModel }
1628 return `import ${[defaultImport, namedImports].filter(Boolean).join(', ')} from '${source}';`;
1629 }
1630 setDeclarationBlockConfig(config) {
1631 this._declarationBlockConfig = config;
1632 }
1633 setVariablesTransformer(variablesTransfomer) {
1634 this._variablesTransfomer = variablesTransfomer;
1635 }
1636 hasScalars() {
1637 return this._hasScalars;
1638 }
1639 hasFederation() {
1640 return this._hasFederation;
1641 }
1642 getRootResolver() {
1643 const name = this.convertName(this.config.allResolversTypeName);
1644 const declarationKind = 'type';
1645 const contextType = `<ContextType = ${this.config.contextType.type}>`;
1646 // This is here because we don't want to break IResolvers, so there is a mapping by default,
1647 // and if the developer is overriding typesPrefix, it won't get generated at all.
1648 const deprecatedIResolvers = !this.config.typesPrefix
1649 ? `
1650/**
1651 * @deprecated
1652 * Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config.
1653 */
1654export type IResolvers${contextType} = ${name}<ContextType>;`
1655 : '';
1656 return [
1657 new DeclarationBlock(this._declarationBlockConfig)
1658 .export()
1659 .asKind(declarationKind)
1660 .withName(name, contextType)
1661 .withBlock(Object.keys(this._collectedResolvers)
1662 .map(schemaTypeName => {
1663 const resolverType = this._collectedResolvers[schemaTypeName];
1664 return indent(this.formatRootResolver(schemaTypeName, resolverType, declarationKind));
1665 })
1666 .join('\n')).string,
1667 deprecatedIResolvers,
1668 ].join('\n');
1669 }
1670 formatRootResolver(schemaTypeName, resolverType, declarationKind) {
1671 return `${schemaTypeName}${this.config.avoidOptionals ? '' : '?'}: ${resolverType}${this.getPunctuation(declarationKind)}`;
1672 }
1673 getAllDirectiveResolvers() {
1674 if (Object.keys(this._collectedDirectiveResolvers).length) {
1675 const declarationKind = 'type';
1676 const name = this.convertName('DirectiveResolvers');
1677 const contextType = `<ContextType = ${this.config.contextType.type}>`;
1678 // This is here because we don't want to break IResolvers, so there is a mapping by default,
1679 // and if the developer is overriding typesPrefix, it won't get generated at all.
1680 const deprecatedIResolvers = !this.config.typesPrefix
1681 ? `
1682/**
1683 * @deprecated
1684 * Use "DirectiveResolvers" root object instead. If you wish to get "IDirectiveResolvers", add "typesPrefix: I" to your config.
1685 */
1686export type IDirectiveResolvers${contextType} = ${name}<ContextType>;`
1687 : '';
1688 return [
1689 new DeclarationBlock(this._declarationBlockConfig)
1690 .export()
1691 .asKind(declarationKind)
1692 .withName(name, contextType)
1693 .withBlock(Object.keys(this._collectedDirectiveResolvers)
1694 .map(schemaTypeName => {
1695 const resolverType = this._collectedDirectiveResolvers[schemaTypeName];
1696 return indent(this.formatRootResolver(schemaTypeName, resolverType, declarationKind));
1697 })
1698 .join('\n')).string,
1699 deprecatedIResolvers,
1700 ].join('\n');
1701 }
1702 return '';
1703 }
1704 Name(node) {
1705 return node.value;
1706 }
1707 ListType(node) {
1708 const asString = node.type;
1709 return this.wrapWithArray(asString);
1710 }
1711 _getScalar(name) {
1712 return `${this.config.namespacedImportName ? this.config.namespacedImportName + '.' : ''}Scalars['${name}']`;
1713 }
1714 NamedType(node) {
1715 const nameStr = node.name;
1716 if (this.config.scalars[nameStr]) {
1717 return this._getScalar(nameStr);
1718 }
1719 return this.convertName(node, null, true);
1720 }
1721 NonNullType(node) {
1722 const asString = node.type;
1723 return asString;
1724 }
1725 markMapperAsUsed(name) {
1726 this._usedMappers[name] = true;
1727 }
1728 getTypeToUse(name) {
1729 const resolversType = this.convertName('ResolversTypes');
1730 return `${resolversType}['${name}']`;
1731 }
1732 getParentTypeToUse(name) {
1733 const resolversType = this.convertName('ResolversParentTypes');
1734 return `${resolversType}['${name}']`;
1735 }
1736 getParentTypeForSignature(node) {
1737 return 'ParentType';
1738 }
1739 transformParentGenericType(parentType) {
1740 return `ParentType extends ${parentType} = ${parentType}`;
1741 }
1742 FieldDefinition(node, key, parent) {
1743 const hasArguments = node.arguments && node.arguments.length > 0;
1744 const declarationKind = 'type';
1745 return (parentName) => {
1746 const original = parent[key];
1747 const baseType = getBaseTypeNode(original.type);
1748 const realType = baseType.name.value;
1749 const parentType = this.schema.getType(parentName);
1750 if (this._federation.skipField({ fieldNode: original, parentType: parentType })) {
1751 return null;
1752 }
1753 const typeToUse = this.getTypeToUse(realType);
1754 const mappedType = this._variablesTransfomer.wrapAstTypeWithModifiers(typeToUse, original.type);
1755 const subscriptionType = this._schema.getSubscriptionType();
1756 const isSubscriptionType = subscriptionType && subscriptionType.name === parentName;
1757 let argsType = hasArguments
1758 ? `${this.convertName(parentName, {
1759 useTypesPrefix: true,
1760 }, true) +
1761 (this.config.addUnderscoreToArgsType ? '_' : '') +
1762 this.convertName(node.name, {
1763 useTypesPrefix: false,
1764 }) +
1765 'Args'}`
1766 : null;
1767 if (argsType !== null) {
1768 const argsToForceRequire = original.arguments.filter(arg => !!arg.defaultValue || arg.type.kind === 'NonNullType');
1769 if (argsToForceRequire.length > 0) {
1770 argsType = this.applyRequireFields(argsType, argsToForceRequire);
1771 }
1772 else if (original.arguments.length > 0) {
1773 argsType = this.applyOptionalFields(argsType, original.arguments);
1774 }
1775 }
1776 const parentTypeSignature = this._federation.transformParentType({
1777 fieldNode: original,
1778 parentType,
1779 parentTypeSignature: this.getParentTypeForSignature(node),
1780 });
1781 const mappedTypeKey = isSubscriptionType ? `${mappedType}, "${node.name}"` : mappedType;
1782 const signature = {
1783 name: node.name,
1784 modifier: this.config.avoidOptionals ? '' : '?',
1785 type: isSubscriptionType ? 'SubscriptionResolver' : 'Resolver',
1786 genericTypes: [
1787 mappedTypeKey,
1788 parentTypeSignature,
1789 this._fieldContextTypeMap[`${parentName}.${node.name}`]
1790 ? this._fieldContextTypeMap[`${parentName}.${node.name}`].type
1791 : 'ContextType',
1792 argsType,
1793 ].filter(f => f),
1794 };
1795 if (this._federation.isResolveReferenceField(node)) {
1796 this._hasFederation = true;
1797 signature.type = 'ReferenceResolver';
1798 if (signature.genericTypes.length >= 3) {
1799 signature.genericTypes = signature.genericTypes.slice(0, 3);
1800 }
1801 }
1802 return indent(`${signature.name}${signature.modifier}: ${signature.type}<${signature.genericTypes.join(', ')}>${this.getPunctuation(declarationKind)}`);
1803 };
1804 }
1805 applyRequireFields(argsType, fields) {
1806 this._globalDeclarations.add(REQUIRE_FIELDS_TYPE);
1807 return `RequireFields<${argsType}, ${fields.map(f => `'${f.name.value}'`).join(' | ')}>`;
1808 }
1809 applyOptionalFields(argsType, fields) {
1810 this._globalDeclarations.add(REQUIRE_FIELDS_TYPE);
1811 return `RequireFields<${argsType}, never>`;
1812 }
1813 ObjectTypeDefinition(node) {
1814 var _a, _b, _c;
1815 const declarationKind = 'type';
1816 const name = this.convertName(node, {
1817 suffix: this.config.resolverTypeSuffix,
1818 });
1819 const typeName = node.name;
1820 const parentType = this.getParentTypeToUse(typeName);
1821 const isRootType = [
1822 (_a = this.schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name,
1823 (_b = this.schema.getMutationType()) === null || _b === void 0 ? void 0 : _b.name,
1824 (_c = this.schema.getSubscriptionType()) === null || _c === void 0 ? void 0 : _c.name,
1825 ].includes(typeName);
1826 const fieldsContent = node.fields.map((f) => f(node.name));
1827 if (!isRootType) {
1828 fieldsContent.push(indent(`__isTypeOf?: IsTypeOfResolverFn<ParentType>${this.getPunctuation(declarationKind)}`));
1829 }
1830 const block = new DeclarationBlock(this._declarationBlockConfig)
1831 .export()
1832 .asKind(declarationKind)
1833 .withName(name, `<ContextType = ${this.config.contextType.type}, ${this.transformParentGenericType(parentType)}>`)
1834 .withBlock(fieldsContent.join('\n'));
1835 this._collectedResolvers[node.name] = name + '<ContextType>';
1836 return block.string;
1837 }
1838 UnionTypeDefinition(node, key, parent) {
1839 const declarationKind = 'type';
1840 const name = this.convertName(node, {
1841 suffix: this.config.resolverTypeSuffix,
1842 });
1843 const originalNode = parent[key];
1844 const possibleTypes = originalNode.types
1845 .map(node => node.name.value)
1846 .map(f => `'${f}'`)
1847 .join(' | ');
1848 this._collectedResolvers[node.name] = name + '<ContextType>';
1849 const parentType = this.getParentTypeToUse(node.name);
1850 return new DeclarationBlock(this._declarationBlockConfig)
1851 .export()
1852 .asKind(declarationKind)
1853 .withName(name, `<ContextType = ${this.config.contextType.type}, ${this.transformParentGenericType(parentType)}>`)
1854 .withBlock(indent(`__resolveType${this.config.optionalResolveType ? '?' : ''}: TypeResolveFn<${possibleTypes}, ParentType, ContextType>${this.getPunctuation(declarationKind)}`)).string;
1855 }
1856 ScalarTypeDefinition(node) {
1857 const nameAsString = node.name;
1858 const baseName = this.getTypeToUse(nameAsString);
1859 if (this._federation.skipScalar(nameAsString)) {
1860 return null;
1861 }
1862 this._hasScalars = true;
1863 this._collectedResolvers[node.name] = 'GraphQLScalarType';
1864 return new DeclarationBlock({
1865 ...this._declarationBlockConfig,
1866 blockTransformer(block) {
1867 return block;
1868 },
1869 })
1870 .export()
1871 .asKind('interface')
1872 .withName(this.convertName(node, {
1873 suffix: 'ScalarConfig',
1874 }), ` extends GraphQLScalarTypeConfig<${baseName}, any>`)
1875 .withBlock(indent(`name: '${node.name}'${this.getPunctuation('interface')}`)).string;
1876 }
1877 DirectiveDefinition(node, key, parent) {
1878 if (this._federation.skipDirective(node.name)) {
1879 return null;
1880 }
1881 const directiveName = this.convertName(node, {
1882 suffix: 'DirectiveResolver',
1883 });
1884 const sourceNode = parent[key];
1885 const hasArguments = sourceNode.arguments && sourceNode.arguments.length > 0;
1886 this._collectedDirectiveResolvers[node.name] = directiveName + '<any, any, ContextType>';
1887 const directiveArgsTypeName = this.convertName(node, {
1888 suffix: 'DirectiveArgs',
1889 });
1890 return [
1891 new DeclarationBlock({
1892 ...this._declarationBlockConfig,
1893 blockTransformer(block) {
1894 return block;
1895 },
1896 })
1897 .export()
1898 .asKind('type')
1899 .withName(directiveArgsTypeName)
1900 .withContent(`{ ${hasArguments ? this._variablesTransfomer.transform(sourceNode.arguments) : ''} }`).string,
1901 new DeclarationBlock({
1902 ...this._declarationBlockConfig,
1903 blockTransformer(block) {
1904 return block;
1905 },
1906 })
1907 .export()
1908 .asKind('type')
1909 .withName(directiveName, `<Result, Parent, ContextType = ${this.config.contextType.type}, Args = ${directiveArgsTypeName}>`)
1910 .withContent(`DirectiveResolverFn<Result, Parent, ContextType, Args>`).string,
1911 ].join('\n');
1912 }
1913 buildEnumResolverContentBlock(node, mappedEnumType) {
1914 throw new Error(`buildEnumResolverContentBlock is not implemented!`);
1915 }
1916 buildEnumResolversExplicitMappedValues(node, valuesMapping) {
1917 throw new Error(`buildEnumResolversExplicitMappedValues is not implemented!`);
1918 }
1919 EnumTypeDefinition(node) {
1920 const rawTypeName = node.name;
1921 // If we have enumValues set, and it's point to an external enum - we need to allow internal values resolvers
1922 // In case we have enumValues set but as explicit values, no need to to do mapping since it's already
1923 // have type validation (the original enum has been modified by base types plugin).
1924 // If we have mapper for that type - we can skip
1925 if (!this.config.mappers[rawTypeName] && !this.config.enumValues[rawTypeName]) {
1926 return null;
1927 }
1928 const name = this.convertName(node, { suffix: this.config.resolverTypeSuffix });
1929 this._collectedResolvers[rawTypeName] = name;
1930 const hasExplicitValues = this.config.enumValues[rawTypeName] && this.config.enumValues[rawTypeName].mappedValues;
1931 return new DeclarationBlock(this._declarationBlockConfig)
1932 .export()
1933 .asKind('type')
1934 .withName(name)
1935 .withContent(hasExplicitValues
1936 ? this.buildEnumResolversExplicitMappedValues(node, this.config.enumValues[rawTypeName].mappedValues)
1937 : this.buildEnumResolverContentBlock(node, this.getTypeToUse(rawTypeName))).string;
1938 }
1939 InterfaceTypeDefinition(node) {
1940 const name = this.convertName(node, {
1941 suffix: this.config.resolverTypeSuffix,
1942 });
1943 const declarationKind = 'type';
1944 const allTypesMap = this._schema.getTypeMap();
1945 const implementingTypes = [];
1946 this._collectedResolvers[node.name] = name + '<ContextType>';
1947 for (const graphqlType of Object.values(allTypesMap)) {
1948 if (graphqlType instanceof GraphQLObjectType) {
1949 const allInterfaces = graphqlType.getInterfaces();
1950 if (allInterfaces.find(int => int.name === node.name)) {
1951 implementingTypes.push(graphqlType.name);
1952 }
1953 }
1954 }
1955 const parentType = this.getParentTypeToUse(node.name);
1956 const possibleTypes = implementingTypes.map(name => `'${name}'`).join(' | ') || 'null';
1957 return new DeclarationBlock(this._declarationBlockConfig)
1958 .export()
1959 .asKind(declarationKind)
1960 .withName(name, `<ContextType = ${this.config.contextType.type}, ${this.transformParentGenericType(parentType)}>`)
1961 .withBlock([
1962 indent(`__resolveType${this.config.optionalResolveType ? '?' : ''}: TypeResolveFn<${possibleTypes}, ParentType, ContextType>${this.getPunctuation(declarationKind)}`),
1963 ...(node.fields || []).map((f) => f(node.name)),
1964 ].join('\n')).string;
1965 }
1966 SchemaDefinition() {
1967 return null;
1968 }
1969}
1970function replacePlaceholder(pattern, typename) {
1971 return pattern.replace('{T}', typename);
1972}
1973function hasPlaceholder(pattern) {
1974 return pattern.includes('{T}');
1975}
1976
1977var DocumentMode;
1978(function (DocumentMode) {
1979 DocumentMode["graphQLTag"] = "graphQLTag";
1980 DocumentMode["documentNode"] = "documentNode";
1981 DocumentMode["documentNodeImportFragments"] = "documentNodeImportFragments";
1982 DocumentMode["external"] = "external";
1983 DocumentMode["string"] = "string";
1984})(DocumentMode || (DocumentMode = {}));
1985const EXTENSIONS_TO_REMOVE = ['.ts', '.tsx', '.js', '.jsx'];
1986class ClientSideBaseVisitor extends BaseVisitor {
1987 constructor(_schema, _fragments, rawConfig, additionalConfig, documents) {
1988 super(rawConfig, {
1989 scalars: buildScalars(_schema, rawConfig.scalars, DEFAULT_SCALARS),
1990 dedupeOperationSuffix: getConfigValue(rawConfig.dedupeOperationSuffix, false),
1991 omitOperationSuffix: getConfigValue(rawConfig.omitOperationSuffix, false),
1992 gqlImport: rawConfig.gqlImport || null,
1993 documentNodeImport: rawConfig.documentNodeImport || null,
1994 noExport: !!rawConfig.noExport,
1995 importOperationTypesFrom: getConfigValue(rawConfig.importOperationTypesFrom, null),
1996 operationResultSuffix: getConfigValue(rawConfig.operationResultSuffix, ''),
1997 documentVariablePrefix: getConfigValue(rawConfig.documentVariablePrefix, ''),
1998 documentVariableSuffix: getConfigValue(rawConfig.documentVariableSuffix, 'Document'),
1999 fragmentVariablePrefix: getConfigValue(rawConfig.fragmentVariablePrefix, ''),
2000 fragmentVariableSuffix: getConfigValue(rawConfig.fragmentVariableSuffix, 'FragmentDoc'),
2001 documentMode: ((rawConfig) => {
2002 if (typeof rawConfig.noGraphQLTag === 'boolean') {
2003 return rawConfig.noGraphQLTag ? DocumentMode.documentNode : DocumentMode.graphQLTag;
2004 }
2005 return getConfigValue(rawConfig.documentMode, DocumentMode.graphQLTag);
2006 })(rawConfig),
2007 importDocumentNodeExternallyFrom: getConfigValue(rawConfig.importDocumentNodeExternallyFrom, ''),
2008 pureMagicComment: getConfigValue(rawConfig.pureMagicComment, false),
2009 ...additionalConfig,
2010 });
2011 this._schema = _schema;
2012 this._fragments = _fragments;
2013 this._collectedOperations = [];
2014 this._documents = [];
2015 this._additionalImports = [];
2016 this._documents = documents;
2017 autoBind(this);
2018 }
2019 _extractFragments(document, withNested = false) {
2020 if (!document) {
2021 return [];
2022 }
2023 const names = new Set();
2024 visit(document, {
2025 enter: {
2026 FragmentSpread: (node) => {
2027 names.add(node.name.value);
2028 if (withNested) {
2029 const foundFragment = this._fragments.find(f => f.name === node.name.value);
2030 if (foundFragment) {
2031 const childItems = this._extractFragments(foundFragment.node, true);
2032 if (childItems && childItems.length > 0) {
2033 for (const item of childItems) {
2034 names.add(item);
2035 }
2036 }
2037 }
2038 }
2039 },
2040 },
2041 });
2042 return Array.from(names);
2043 }
2044 _transformFragments(document) {
2045 const includeNestedFragments = this.config.documentMode === DocumentMode.documentNode;
2046 return this._extractFragments(document, includeNestedFragments).map(document => this.getFragmentVariableName(document));
2047 }
2048 _includeFragments(fragments) {
2049 if (fragments && fragments.length > 0) {
2050 if (this.config.documentMode === DocumentMode.documentNode) {
2051 return this._fragments
2052 .filter(f => fragments.includes(this.getFragmentVariableName(f.name)))
2053 .map(fragment => print(fragment.node))
2054 .join('\n');
2055 }
2056 else if (this.config.documentMode === DocumentMode.documentNodeImportFragments) {
2057 return '';
2058 }
2059 else {
2060 return `${fragments.map(name => '${' + name + '}').join('\n')}`;
2061 }
2062 }
2063 return '';
2064 }
2065 _prepareDocument(documentStr) {
2066 return documentStr;
2067 }
2068 _gql(node) {
2069 const fragments = this._transformFragments(node);
2070 const doc = this._prepareDocument(`
2071 ${print(node).split('\\').join('\\\\') /* Re-escape escaped values in GraphQL syntax */}
2072 ${this._includeFragments(fragments)}`);
2073 if (this.config.documentMode === DocumentMode.documentNode) {
2074 const gqlObj = gqlTag([doc]);
2075 if (gqlObj && gqlObj.loc) {
2076 delete gqlObj.loc;
2077 }
2078 return JSON.stringify(gqlObj);
2079 }
2080 else if (this.config.documentMode === DocumentMode.documentNodeImportFragments) {
2081 const gqlObj = gqlTag([doc]);
2082 if (gqlObj && gqlObj.loc) {
2083 delete gqlObj.loc;
2084 }
2085 if (fragments.length > 0) {
2086 const definitions = [
2087 ...gqlObj.definitions.map(t => JSON.stringify(t)),
2088 ...fragments.map(name => `...${name}.definitions`),
2089 ].join();
2090 return `{"kind":"${Kind.DOCUMENT}","definitions":[${definitions}]}`;
2091 }
2092 return JSON.stringify(gqlObj);
2093 }
2094 else if (this.config.documentMode === DocumentMode.string) {
2095 return '`' + doc + '`';
2096 }
2097 const gqlImport = this._parseImport(this.config.gqlImport || 'graphql-tag');
2098 return (gqlImport.propName || 'gql') + '`' + doc + '`';
2099 }
2100 _generateFragment(fragmentDocument) {
2101 const name = this.getFragmentVariableName(fragmentDocument);
2102 const isDocumentNode = this.config.documentMode === DocumentMode.documentNode ||
2103 this.config.documentMode === DocumentMode.documentNodeImportFragments;
2104 const fragmentResultType = this.convertName(fragmentDocument.name.value, {
2105 useTypesPrefix: true,
2106 suffix: this.getFragmentSuffix(fragmentDocument),
2107 });
2108 return `export const ${name}${isDocumentNode ? `: ${this.getDocumentNodeSignature(fragmentResultType, 'unknown', fragmentDocument)}` : ''} =${this.config.pureMagicComment ? ' /*#__PURE__*/' : ''} ${this._gql(fragmentDocument)};`;
2109 }
2110 get fragmentsGraph() {
2111 const graph = new DepGraph({ circular: true });
2112 for (const fragment of this._fragments) {
2113 if (graph.hasNode(fragment.name)) {
2114 const cachedAsString = print(graph.getNodeData(fragment.name).node);
2115 const asString = print(fragment.node);
2116 if (cachedAsString !== asString) {
2117 throw new Error(`Duplicated fragment called '${fragment.name}'!`);
2118 }
2119 }
2120 graph.addNode(fragment.name, fragment);
2121 }
2122 this._fragments.forEach(fragment => {
2123 const depends = this._extractFragments(fragment.node);
2124 if (depends && depends.length > 0) {
2125 depends.forEach(name => {
2126 graph.addDependency(fragment.name, name);
2127 });
2128 }
2129 });
2130 return graph;
2131 }
2132 get fragments() {
2133 if (this._fragments.length === 0 || this.config.documentMode === DocumentMode.external) {
2134 return '';
2135 }
2136 const graph = this.fragmentsGraph;
2137 const orderedDeps = graph.overallOrder();
2138 const localFragments = orderedDeps
2139 .filter(name => !graph.getNodeData(name).isExternal)
2140 .map(name => this._generateFragment(graph.getNodeData(name).node));
2141 return localFragments.join('\n');
2142 }
2143 _parseImport(importStr) {
2144 // This is a special case when we want to ignore importing, and just use `gql` provided from somewhere else
2145 // Plugins that uses that will need to ensure to add import/declaration for the gql identifier
2146 if (importStr === 'gql') {
2147 return {
2148 moduleName: null,
2149 propName: 'gql',
2150 };
2151 }
2152 // This is a special use case, when we don't want this plugin to manage the import statement
2153 // of the gql tag. In this case, we provide something like `Namespace.gql` and it will be used instead.
2154 if (importStr.includes('.gql')) {
2155 return {
2156 moduleName: null,
2157 propName: importStr,
2158 };
2159 }
2160 const [moduleName, propName] = importStr.split('#');
2161 return {
2162 moduleName,
2163 propName,
2164 };
2165 }
2166 _generateImport({ moduleName, propName }, varName, isTypeImport) {
2167 const typeImport = isTypeImport && this.config.useTypeImports ? 'import type' : 'import';
2168 const propAlias = propName === varName ? '' : ` as ${varName}`;
2169 if (moduleName) {
2170 return `${typeImport} ${propName ? `{ ${propName}${propAlias} }` : varName} from '${moduleName}';`;
2171 }
2172 return null;
2173 }
2174 clearExtension(path) {
2175 const extension = extname(path);
2176 if (EXTENSIONS_TO_REMOVE.includes(extension)) {
2177 return path.replace(/\.[^/.]+$/, '');
2178 }
2179 return path;
2180 }
2181 getImports(options = {}) {
2182 const imports = [...this._additionalImports];
2183 switch (this.config.documentMode) {
2184 case DocumentMode.documentNode:
2185 case DocumentMode.documentNodeImportFragments: {
2186 const documentNodeImport = this._parseImport(this.config.documentNodeImport || 'graphql#DocumentNode');
2187 const tagImport = this._generateImport(documentNodeImport, 'DocumentNode', true);
2188 if (tagImport) {
2189 imports.push(tagImport);
2190 }
2191 break;
2192 }
2193 case DocumentMode.graphQLTag: {
2194 const gqlImport = this._parseImport(this.config.gqlImport || 'graphql-tag');
2195 const tagImport = this._generateImport(gqlImport, 'gql', false);
2196 if (tagImport) {
2197 imports.push(tagImport);
2198 }
2199 break;
2200 }
2201 case DocumentMode.external: {
2202 if (this._collectedOperations.length > 0) {
2203 if (this.config.importDocumentNodeExternallyFrom === 'near-operation-file' && this._documents.length === 1) {
2204 imports.push(`import * as Operations from './${this.clearExtension(basename(this._documents[0].location))}';`);
2205 }
2206 else {
2207 imports.push(`import * as Operations from '${this.clearExtension(this.config.importDocumentNodeExternallyFrom)}';`);
2208 }
2209 }
2210 break;
2211 }
2212 }
2213 if (!options.excludeFragments && !this.config.globalNamespace) {
2214 const { documentMode, fragmentImports } = this.config;
2215 if (documentMode === DocumentMode.graphQLTag ||
2216 documentMode === DocumentMode.string ||
2217 documentMode === DocumentMode.documentNodeImportFragments) {
2218 imports.push(...fragmentImports.map(fragmentImport => generateFragmentImportStatement(fragmentImport, 'document')));
2219 }
2220 }
2221 return imports;
2222 }
2223 buildOperation(node, documentVariableName, operationType, operationResultType, operationVariablesTypes) {
2224 return null;
2225 }
2226 getDocumentNodeSignature(resultType, variablesTypes, node) {
2227 return `DocumentNode`;
2228 }
2229 OperationDefinition(node) {
2230 if (!node.name || !node.name.value) {
2231 return null;
2232 }
2233 this._collectedOperations.push(node);
2234 const documentVariableName = this.convertName(node, {
2235 suffix: this.config.documentVariableSuffix,
2236 prefix: this.config.documentVariablePrefix,
2237 useTypesPrefix: false,
2238 });
2239 const operationType = pascalCase(node.operation);
2240 const operationTypeSuffix = this.getOperationSuffix(node, operationType);
2241 const operationResultType = this.convertName(node, {
2242 suffix: operationTypeSuffix + this._parsedConfig.operationResultSuffix,
2243 });
2244 const operationVariablesTypes = this.convertName(node, {
2245 suffix: operationTypeSuffix + 'Variables',
2246 });
2247 let documentString = '';
2248 if (this.config.documentMode !== DocumentMode.external) {
2249 const isDocumentNode = this.config.documentMode === DocumentMode.documentNode ||
2250 this.config.documentMode === DocumentMode.documentNodeImportFragments;
2251 documentString = `${this.config.noExport ? '' : 'export'} const ${documentVariableName}${isDocumentNode ? `: ${this.getDocumentNodeSignature(operationResultType, operationVariablesTypes, node)}` : ''} =${this.config.pureMagicComment ? ' /*#__PURE__*/' : ''} ${this._gql(node)};`;
2252 }
2253 const additional = this.buildOperation(node, documentVariableName, operationType, operationResultType, operationVariablesTypes);
2254 return [documentString, additional].filter(a => a).join('\n');
2255 }
2256}
2257
2258function isMetadataFieldName(name) {
2259 return ['__schema', '__type'].includes(name);
2260}
2261const metadataFieldMap = {
2262 __schema: SchemaMetaFieldDef,
2263 __type: TypeMetaFieldDef,
2264};
2265class SelectionSetToObject {
2266 constructor(_processor, _scalars, _schema, _convertName, _getFragmentSuffix, _loadedFragments, _config, _parentSchemaType, _selectionSet) {
2267 this._processor = _processor;
2268 this._scalars = _scalars;
2269 this._schema = _schema;
2270 this._convertName = _convertName;
2271 this._getFragmentSuffix = _getFragmentSuffix;
2272 this._loadedFragments = _loadedFragments;
2273 this._config = _config;
2274 this._parentSchemaType = _parentSchemaType;
2275 this._selectionSet = _selectionSet;
2276 this._primitiveFields = [];
2277 this._primitiveAliasedFields = [];
2278 this._linksFields = [];
2279 this._queriedForTypename = false;
2280 autoBind(this);
2281 }
2282 createNext(parentSchemaType, selectionSet) {
2283 return new SelectionSetToObject(this._processor, this._scalars, this._schema, this._convertName.bind(this), this._getFragmentSuffix.bind(this), this._loadedFragments, this._config, parentSchemaType, selectionSet);
2284 }
2285 /**
2286 * traverse the inline fragment nodes recursively for colleting the selectionSets on each type
2287 */
2288 _collectInlineFragments(parentType, nodes, types) {
2289 if (isListType(parentType) || isNonNullType(parentType)) {
2290 return this._collectInlineFragments(parentType.ofType, nodes, types);
2291 }
2292 else if (isObjectType(parentType)) {
2293 for (const node of nodes) {
2294 const typeOnSchema = node.typeCondition ? this._schema.getType(node.typeCondition.name.value) : parentType;
2295 const { fields, inlines, spreads } = separateSelectionSet(node.selectionSet.selections);
2296 const spreadsUsage = this.buildFragmentSpreadsUsage(spreads);
2297 if (isObjectType(typeOnSchema)) {
2298 this._appendToTypeMap(types, typeOnSchema.name, fields);
2299 this._appendToTypeMap(types, typeOnSchema.name, spreadsUsage[typeOnSchema.name]);
2300 this._collectInlineFragments(typeOnSchema, inlines, types);
2301 }
2302 else if (isInterfaceType(typeOnSchema) && parentType.isTypeOf(typeOnSchema, null, null)) {
2303 this._appendToTypeMap(types, parentType.name, fields);
2304 this._appendToTypeMap(types, parentType.name, spreadsUsage[parentType.name]);
2305 this._collectInlineFragments(typeOnSchema, inlines, types);
2306 }
2307 }
2308 }
2309 else if (isInterfaceType(parentType)) {
2310 const possibleTypes = getPossibleTypes(this._schema, parentType);
2311 for (const node of nodes) {
2312 const schemaType = node.typeCondition ? this._schema.getType(node.typeCondition.name.value) : parentType;
2313 const { fields, inlines, spreads } = separateSelectionSet(node.selectionSet.selections);
2314 const spreadsUsage = this.buildFragmentSpreadsUsage(spreads);
2315 if (isObjectType(schemaType) && possibleTypes.find(possibleType => possibleType.name === schemaType.name)) {
2316 this._appendToTypeMap(types, schemaType.name, fields);
2317 this._appendToTypeMap(types, schemaType.name, spreadsUsage[schemaType.name]);
2318 this._collectInlineFragments(schemaType, inlines, types);
2319 }
2320 else if (isInterfaceType(schemaType) && schemaType.name === parentType.name) {
2321 for (const possibleType of possibleTypes) {
2322 this._appendToTypeMap(types, possibleType.name, fields);
2323 this._appendToTypeMap(types, possibleType.name, spreadsUsage[possibleType.name]);
2324 this._collectInlineFragments(schemaType, inlines, types);
2325 }
2326 }
2327 else {
2328 for (const possibleType of possibleTypes) {
2329 this._appendToTypeMap(types, possibleType.name, fields);
2330 this._appendToTypeMap(types, possibleType.name, spreadsUsage[possibleType.name]);
2331 }
2332 }
2333 }
2334 }
2335 else if (isUnionType(parentType)) {
2336 const possibleTypes = parentType.getTypes();
2337 for (const node of nodes) {
2338 const schemaType = node.typeCondition ? this._schema.getType(node.typeCondition.name.value) : parentType;
2339 const { fields, inlines, spreads } = separateSelectionSet(node.selectionSet.selections);
2340 const spreadsUsage = this.buildFragmentSpreadsUsage(spreads);
2341 if (isObjectType(schemaType) && possibleTypes.find(possibleType => possibleType.name === schemaType.name)) {
2342 this._appendToTypeMap(types, schemaType.name, fields);
2343 this._appendToTypeMap(types, schemaType.name, spreadsUsage[schemaType.name]);
2344 this._collectInlineFragments(schemaType, inlines, types);
2345 }
2346 else if (isInterfaceType(schemaType)) {
2347 const possibleInterfaceTypes = getPossibleTypes(this._schema, schemaType);
2348 for (const possibleType of possibleTypes) {
2349 if (possibleInterfaceTypes.find(possibleInterfaceType => possibleInterfaceType.name === possibleType.name)) {
2350 this._appendToTypeMap(types, possibleType.name, fields);
2351 this._appendToTypeMap(types, possibleType.name, spreadsUsage[possibleType.name]);
2352 this._collectInlineFragments(schemaType, inlines, types);
2353 }
2354 }
2355 }
2356 else {
2357 for (const possibleType of possibleTypes) {
2358 this._appendToTypeMap(types, possibleType.name, fields);
2359 this._appendToTypeMap(types, possibleType.name, spreadsUsage[possibleType.name]);
2360 }
2361 }
2362 }
2363 }
2364 }
2365 _createInlineFragmentForFieldNodes(parentType, fieldNodes) {
2366 return {
2367 kind: Kind.INLINE_FRAGMENT,
2368 typeCondition: {
2369 kind: Kind.NAMED_TYPE,
2370 name: {
2371 kind: Kind.NAME,
2372 value: parentType.name,
2373 },
2374 },
2375 directives: [],
2376 selectionSet: {
2377 kind: Kind.SELECTION_SET,
2378 selections: fieldNodes,
2379 },
2380 };
2381 }
2382 buildFragmentSpreadsUsage(spreads) {
2383 const selectionNodesByTypeName = {};
2384 for (const spread of spreads) {
2385 const fragmentSpreadObject = this._loadedFragments.find(lf => lf.name === spread.name.value);
2386 if (fragmentSpreadObject) {
2387 const schemaType = this._schema.getType(fragmentSpreadObject.onType);
2388 const possibleTypesForFragment = getPossibleTypes(this._schema, schemaType);
2389 for (const possibleType of possibleTypesForFragment) {
2390 const fragmentSuffix = this._getFragmentSuffix(spread.name.value);
2391 const usage = this.buildFragmentTypeName(spread.name.value, fragmentSuffix, possibleTypesForFragment.length === 1 ? null : possibleType.name);
2392 if (!selectionNodesByTypeName[possibleType.name]) {
2393 selectionNodesByTypeName[possibleType.name] = [];
2394 }
2395 selectionNodesByTypeName[possibleType.name].push(usage);
2396 }
2397 }
2398 }
2399 return selectionNodesByTypeName;
2400 }
2401 flattenSelectionSet(selections) {
2402 const selectionNodesByTypeName = new Map();
2403 const inlineFragmentSelections = [];
2404 const fieldNodes = [];
2405 const fragmentSpreads = [];
2406 for (const selection of selections) {
2407 switch (selection.kind) {
2408 case Kind.FIELD:
2409 fieldNodes.push(selection);
2410 break;
2411 case Kind.INLINE_FRAGMENT:
2412 inlineFragmentSelections.push(selection);
2413 break;
2414 case Kind.FRAGMENT_SPREAD:
2415 fragmentSpreads.push(selection);
2416 break;
2417 }
2418 }
2419 if (fieldNodes.length) {
2420 inlineFragmentSelections.push(this._createInlineFragmentForFieldNodes(this._parentSchemaType, fieldNodes));
2421 }
2422 this._collectInlineFragments(this._parentSchemaType, inlineFragmentSelections, selectionNodesByTypeName);
2423 const fragmentsUsage = this.buildFragmentSpreadsUsage(fragmentSpreads);
2424 Object.keys(fragmentsUsage).forEach(typeName => {
2425 this._appendToTypeMap(selectionNodesByTypeName, typeName, fragmentsUsage[typeName]);
2426 });
2427 return selectionNodesByTypeName;
2428 }
2429 _appendToTypeMap(types, typeName, nodes) {
2430 if (!types.has(typeName)) {
2431 types.set(typeName, []);
2432 }
2433 if (nodes && nodes.length > 0) {
2434 types.get(typeName).push(...nodes);
2435 }
2436 }
2437 _buildGroupedSelections() {
2438 if (!this._selectionSet || !this._selectionSet.selections || this._selectionSet.selections.length === 0) {
2439 return {};
2440 }
2441 const selectionNodesByTypeName = this.flattenSelectionSet(this._selectionSet.selections);
2442 const grouped = getPossibleTypes(this._schema, this._parentSchemaType).reduce((prev, type) => {
2443 const typeName = type.name;
2444 const schemaType = this._schema.getType(typeName);
2445 if (!isObjectType(schemaType)) {
2446 throw new TypeError(`Invalid state! Schema type ${typeName} is not a valid GraphQL object!`);
2447 }
2448 const selectionNodes = selectionNodesByTypeName.get(typeName) || [];
2449 if (!prev[typeName]) {
2450 prev[typeName] = [];
2451 }
2452 const transformedSet = this.buildSelectionSetString(schemaType, selectionNodes);
2453 if (transformedSet) {
2454 prev[typeName].push(transformedSet);
2455 }
2456 return prev;
2457 }, {});
2458 return grouped;
2459 }
2460 buildSelectionSetString(parentSchemaType, selectionNodes) {
2461 const primitiveFields = new Map();
2462 const primitiveAliasFields = new Map();
2463 const linkFieldSelectionSets = new Map();
2464 let requireTypename = false;
2465 const fragmentsSpreadUsages = [];
2466 for (const selectionNode of selectionNodes) {
2467 if (typeof selectionNode === 'string') {
2468 fragmentsSpreadUsages.push(selectionNode);
2469 }
2470 else if (selectionNode.kind === 'Field') {
2471 if (!selectionNode.selectionSet) {
2472 if (selectionNode.alias) {
2473 primitiveAliasFields.set(selectionNode.alias.value, selectionNode);
2474 }
2475 else if (selectionNode.name.value === '__typename') {
2476 requireTypename = true;
2477 }
2478 else {
2479 primitiveFields.set(selectionNode.name.value, selectionNode);
2480 }
2481 }
2482 else {
2483 let selectedField = null;
2484 const fields = parentSchemaType.getFields();
2485 selectedField = fields[selectionNode.name.value];
2486 if (isMetadataFieldName(selectionNode.name.value)) {
2487 selectedField = metadataFieldMap[selectionNode.name.value];
2488 }
2489 if (!selectedField) {
2490 continue;
2491 }
2492 const fieldName = getFieldNodeNameValue(selectionNode);
2493 let linkFieldNode = linkFieldSelectionSets.get(fieldName);
2494 if (!linkFieldNode) {
2495 linkFieldNode = {
2496 selectedFieldType: selectedField.type,
2497 field: selectionNode,
2498 };
2499 linkFieldSelectionSets.set(fieldName, linkFieldNode);
2500 }
2501 else {
2502 mergeSelectionSets(linkFieldNode.field.selectionSet, selectionNode.selectionSet);
2503 }
2504 }
2505 }
2506 }
2507 const linkFields = [];
2508 for (const { field, selectedFieldType } of linkFieldSelectionSets.values()) {
2509 const realSelectedFieldType = getBaseType(selectedFieldType);
2510 const selectionSet = this.createNext(realSelectedFieldType, field.selectionSet);
2511 linkFields.push({
2512 alias: field.alias ? this._processor.config.formatNamedField(field.alias.value, selectedFieldType) : undefined,
2513 name: this._processor.config.formatNamedField(field.name.value, selectedFieldType),
2514 type: realSelectedFieldType.name,
2515 selectionSet: this._processor.config.wrapTypeWithModifiers(selectionSet.transformSelectionSet().split(`\n`).join(`\n `), selectedFieldType),
2516 });
2517 }
2518 const typeInfoField = this.buildTypeNameField(parentSchemaType, this._config.nonOptionalTypename, this._config.addTypename, requireTypename, this._config.skipTypeNameForRoot);
2519 const transformed = [
2520 ...(typeInfoField ? this._processor.transformTypenameField(typeInfoField.type, typeInfoField.name) : []),
2521 ...this._processor.transformPrimitiveFields(parentSchemaType, Array.from(primitiveFields.values()).map(field => field.name.value)),
2522 ...this._processor.transformAliasesPrimitiveFields(parentSchemaType, Array.from(primitiveAliasFields.values()).map(field => ({
2523 alias: field.alias.value,
2524 fieldName: field.name.value,
2525 }))),
2526 ...this._processor.transformLinkFields(linkFields),
2527 ].filter(Boolean);
2528 const allStrings = transformed.filter(t => typeof t === 'string');
2529 const allObjectsMerged = transformed
2530 .filter(t => typeof t !== 'string')
2531 .map((t) => `${t.name}: ${t.type}`);
2532 let mergedObjectsAsString = null;
2533 if (allObjectsMerged.length > 0) {
2534 mergedObjectsAsString = this._processor.buildFieldsIntoObject(allObjectsMerged);
2535 }
2536 const fields = [...allStrings, mergedObjectsAsString, ...fragmentsSpreadUsages].filter(Boolean);
2537 return this._processor.buildSelectionSetFromStrings(fields);
2538 }
2539 isRootType(type) {
2540 const rootType = [this._schema.getQueryType(), this._schema.getMutationType(), this._schema.getSubscriptionType()]
2541 .filter(Boolean)
2542 .map(t => t.name);
2543 return rootType.includes(type.name);
2544 }
2545 buildTypeNameField(type, nonOptionalTypename = this._config.nonOptionalTypename, addTypename = this._config.addTypename, queriedForTypename = this._queriedForTypename, skipTypeNameForRoot = this._config.skipTypeNameForRoot) {
2546 if (this.isRootType(type) && skipTypeNameForRoot && !queriedForTypename) {
2547 return null;
2548 }
2549 if (nonOptionalTypename || addTypename || queriedForTypename) {
2550 const optionalTypename = !queriedForTypename && !nonOptionalTypename;
2551 return {
2552 name: `${this._processor.config.formatNamedField('__typename')}${optionalTypename ? '?' : ''}`,
2553 type: `'${type.name}'`,
2554 };
2555 }
2556 return null;
2557 }
2558 transformSelectionSet() {
2559 const grouped = this._buildGroupedSelections();
2560 return Object.keys(grouped)
2561 .map(typeName => {
2562 const relevant = grouped[typeName].filter(Boolean);
2563 if (relevant.length === 0) {
2564 return null;
2565 }
2566 else if (relevant.length === 1) {
2567 return relevant[0];
2568 }
2569 else {
2570 return `( ${relevant.join(' & ')} )`;
2571 }
2572 })
2573 .filter(Boolean)
2574 .join(' | ');
2575 }
2576 transformFragmentSelectionSetToTypes(fragmentName, fragmentSuffix, declarationBlockConfig) {
2577 const grouped = this._buildGroupedSelections();
2578 const subTypes = Object.keys(grouped)
2579 .map(typeName => {
2580 const possibleFields = grouped[typeName].filter(Boolean);
2581 if (possibleFields.length === 0) {
2582 return null;
2583 }
2584 const declarationName = this.buildFragmentTypeName(fragmentName, fragmentSuffix, typeName);
2585 return { name: declarationName, content: possibleFields.join(' & ') };
2586 })
2587 .filter(Boolean);
2588 if (subTypes.length === 1) {
2589 return new DeclarationBlock(declarationBlockConfig)
2590 .export()
2591 .asKind('type')
2592 .withName(this.buildFragmentTypeName(fragmentName, fragmentSuffix))
2593 .withContent(subTypes[0].content).string;
2594 }
2595 return [
2596 ...subTypes.map(t => new DeclarationBlock(declarationBlockConfig)
2597 .export(this._config.exportFragmentSpreadSubTypes)
2598 .asKind('type')
2599 .withName(t.name)
2600 .withContent(t.content).string),
2601 new DeclarationBlock(declarationBlockConfig)
2602 .export()
2603 .asKind('type')
2604 .withName(this.buildFragmentTypeName(fragmentName, fragmentSuffix))
2605 .withContent(subTypes.map(t => t.name).join(' | ')).string,
2606 ].join('\n');
2607 }
2608 buildFragmentTypeName(name, suffix, typeName = '') {
2609 return this._convertName(name, {
2610 useTypesPrefix: true,
2611 suffix: typeName ? `_${typeName}_${suffix}` : suffix,
2612 });
2613 }
2614}
2615
2616class BaseSelectionSetProcessor {
2617 constructor(config) {
2618 this.config = config;
2619 }
2620 buildFieldsIntoObject(allObjectsMerged) {
2621 return `{ ${allObjectsMerged.join(', ')} }`;
2622 }
2623 buildSelectionSetFromStrings(pieces) {
2624 if (pieces.length === 0) {
2625 return null;
2626 }
2627 else if (pieces.length === 1) {
2628 return pieces[0];
2629 }
2630 else {
2631 return `(\n ${pieces.join(`\n & `)}\n)`;
2632 }
2633 }
2634 transformPrimitiveFields(schemaType, fields) {
2635 throw new Error(`Please override "transformPrimitiveFields" as part of your BaseSelectionSetProcessor implementation!`);
2636 }
2637 transformAliasesPrimitiveFields(schemaType, fields) {
2638 throw new Error(`Please override "transformAliasesPrimitiveFields" as part of your BaseSelectionSetProcessor implementation!`);
2639 }
2640 transformLinkFields(fields) {
2641 throw new Error(`Please override "transformLinkFields" as part of your BaseSelectionSetProcessor implementation!`);
2642 }
2643 transformTypenameField(type, name) {
2644 throw new Error(`Please override "transformTypenameField" as part of your BaseSelectionSetProcessor implementation!`);
2645 }
2646}
2647
2648class PreResolveTypesProcessor extends BaseSelectionSetProcessor {
2649 transformTypenameField(type, name) {
2650 return [
2651 {
2652 type,
2653 name,
2654 },
2655 ];
2656 }
2657 transformPrimitiveFields(schemaType, fields) {
2658 if (fields.length === 0) {
2659 return [];
2660 }
2661 return fields.map(field => {
2662 const fieldObj = schemaType.getFields()[field];
2663 const baseType = getBaseType(fieldObj.type);
2664 let typeToUse = baseType.name;
2665 if (isEnumType(baseType)) {
2666 typeToUse =
2667 (this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '') +
2668 this.config.convertName(baseType.name, { useTypesPrefix: this.config.enumPrefix });
2669 }
2670 else if (this.config.scalars[baseType.name]) {
2671 typeToUse = this.config.scalars[baseType.name];
2672 }
2673 const name = this.config.formatNamedField(field, fieldObj.type);
2674 const wrappedType = this.config.wrapTypeWithModifiers(typeToUse, fieldObj.type);
2675 return {
2676 name,
2677 type: wrappedType,
2678 };
2679 });
2680 }
2681 transformAliasesPrimitiveFields(schemaType, fields) {
2682 if (fields.length === 0) {
2683 return [];
2684 }
2685 return fields.map(aliasedField => {
2686 if (aliasedField.fieldName === '__typename') {
2687 const name = this.config.formatNamedField(aliasedField.alias, null);
2688 return {
2689 name,
2690 type: `'${schemaType.name}'`,
2691 };
2692 }
2693 else {
2694 const fieldObj = schemaType.getFields()[aliasedField.fieldName];
2695 const baseType = getBaseType(fieldObj.type);
2696 let typeToUse = this.config.scalars[baseType.name] || baseType.name;
2697 if (isEnumType(baseType)) {
2698 typeToUse =
2699 (this.config.namespacedImportName ? `${this.config.namespacedImportName}.` : '') +
2700 this.config.convertName(baseType.name, { useTypesPrefix: this.config.enumPrefix });
2701 }
2702 const name = this.config.formatNamedField(aliasedField.alias, fieldObj.type);
2703 const wrappedType = this.config.wrapTypeWithModifiers(typeToUse, fieldObj.type);
2704 return {
2705 name,
2706 type: wrappedType,
2707 };
2708 }
2709 });
2710 }
2711 transformLinkFields(fields) {
2712 if (fields.length === 0) {
2713 return [];
2714 }
2715 return fields.map(field => ({
2716 name: field.alias || field.name,
2717 type: field.selectionSet,
2718 }));
2719 }
2720}
2721
2722function optimizeOperations(schema, documents, options) {
2723 const newDocuments = optimizeDocuments(schema, documents.map(s => s.document), options);
2724 return newDocuments.map(document => ({
2725 location: 'optimized by relay',
2726 document,
2727 }));
2728}
2729
2730export { BaseDocumentsVisitor, BaseResolversVisitor, BaseSelectionSetProcessor, BaseTypesVisitor, BaseVisitor, ClientSideBaseVisitor, DEFAULT_AVOID_OPTIONALS, DEFAULT_DECLARATION_KINDS, DEFAULT_SCALARS, DeclarationBlock, DocumentMode, OMIT_TYPE, OperationVariablesToObject, PreResolveTypesProcessor, REQUIRE_FIELDS_TYPE, SelectionSetToObject, block, breakLine, buildScalars, clearExtension, convertFactory, convertNameParts, fixLocalFilePath, generateFragmentImportStatement, generateImportStatement, getBaseTypeNode, getConfigValue, getFieldNodeNameValue, getPossibleTypes, getRootTypeNames, indent, indentMultiline, isExternalMapper, isExternalMapperType, isRootType, mergeSelectionSets, normalizeAvoidOptionals, normalizeDeclarationKind, optimizeOperations, parseEnumValues, parseMapper, quoteIfNeeded, resolveImportSource, resolveRelativeImport, separateSelectionSet, stripMapperTypeInterpolation, transformComment, transformMappers, wrapTypeWithModifiers, wrapWithSingleQuotes };
2731//# sourceMappingURL=index.esm.js.map