UNPKG

31.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5const utils = require('@graphql-tools/utils');
6const path = require('path');
7const fsExtra = require('fs-extra');
8const process = require('process');
9const graphql = require('graphql');
10const _import = require('@graphql-tools/import');
11
12function mergeArguments(args1, args2, config) {
13 const result = deduplicateArguments([].concat(args2, args1).filter(a => a));
14 if (config && config.sort) {
15 result.sort(utils.compareNodes);
16 }
17 return result;
18}
19function deduplicateArguments(args) {
20 return args.reduce((acc, current) => {
21 const dup = acc.find(arg => arg.name.value === current.name.value);
22 if (!dup) {
23 return acc.concat([current]);
24 }
25 return acc;
26 }, []);
27}
28
29let commentsRegistry = {};
30function resetComments() {
31 commentsRegistry = {};
32}
33function collectComment(node) {
34 const entityName = node.name.value;
35 pushComment(node, entityName);
36 switch (node.kind) {
37 case 'EnumTypeDefinition':
38 node.values.forEach(value => {
39 pushComment(value, entityName, value.name.value);
40 });
41 break;
42 case 'ObjectTypeDefinition':
43 case 'InputObjectTypeDefinition':
44 case 'InterfaceTypeDefinition':
45 if (node.fields) {
46 node.fields.forEach((field) => {
47 pushComment(field, entityName, field.name.value);
48 if (isFieldDefinitionNode(field) && field.arguments) {
49 field.arguments.forEach(arg => {
50 pushComment(arg, entityName, field.name.value, arg.name.value);
51 });
52 }
53 });
54 }
55 break;
56 }
57}
58function pushComment(node, entity, field, argument) {
59 const comment = graphql.getDescription(node, { commentDescriptions: true });
60 if (typeof comment !== 'string' || comment.length === 0) {
61 return;
62 }
63 const keys = [entity];
64 if (field) {
65 keys.push(field);
66 if (argument) {
67 keys.push(argument);
68 }
69 }
70 const path = keys.join('.');
71 if (!commentsRegistry[path]) {
72 commentsRegistry[path] = [];
73 }
74 commentsRegistry[path].push(comment);
75}
76function printComment(comment) {
77 return '\n# ' + comment.replace(/\n/g, '\n# ');
78}
79/**
80 * Copyright (c) 2015-present, Facebook, Inc.
81 *
82 * This source code is licensed under the MIT license found in the
83 * LICENSE file in the root directory of this source tree.
84 */
85/**
86 * NOTE: ==> This file has been modified just to add comments to the printed AST
87 * This is a temp measure, we will move to using the original non modified printer.js ASAP.
88 */
89// import { visit, VisitFn } from 'graphql/language/visitor';
90/**
91 * Given maybeArray, print an empty string if it is null or empty, otherwise
92 * print all items together separated by separator if provided
93 */
94function join(maybeArray, separator) {
95 return maybeArray ? maybeArray.filter(x => x).join(separator || '') : '';
96}
97function addDescription(cb) {
98 return (node, _key, _parent, path, ancestors) => {
99 const keys = [];
100 const parent = path.reduce((prev, key) => {
101 if (['fields', 'arguments', 'values'].includes(key)) {
102 keys.push(prev.name.value);
103 }
104 return prev[key];
105 }, ancestors[0]);
106 const key = [...keys, parent.name.value].join('.');
107 const items = [];
108 if (commentsRegistry[key]) {
109 items.push(...commentsRegistry[key]);
110 }
111 return join([...items.map(printComment), node.description, cb(node)], '\n');
112 };
113}
114function indent(maybeString) {
115 return maybeString && ` ${maybeString.replace(/\n/g, '\n ')}`;
116}
117/**
118 * Given array, print each item on its own line, wrapped in an
119 * indented "{ }" block.
120 */
121function block(array) {
122 return array && array.length !== 0 ? `{\n${indent(join(array, '\n'))}\n}` : '';
123}
124/**
125 * If maybeString is not null or empty, then wrap with start and end, otherwise
126 * print an empty string.
127 */
128function wrap(start, maybeString, end) {
129 return maybeString ? start + maybeString + (end || '') : '';
130}
131/**
132 * Print a block string in the indented block form by adding a leading and
133 * trailing blank line. However, if a block string starts with whitespace and is
134 * a single-line, adding a leading blank line would strip that whitespace.
135 */
136function printBlockString(value, isDescription) {
137 const escaped = value.replace(/"""/g, '\\"""');
138 return (value[0] === ' ' || value[0] === '\t') && value.indexOf('\n') === -1
139 ? `"""${escaped.replace(/"$/, '"\n')}"""`
140 : `"""\n${isDescription ? escaped : indent(escaped)}\n"""`;
141}
142/**
143 * Converts an AST into a string, using one set of reasonable
144 * formatting rules.
145 */
146function printWithComments(ast) {
147 return graphql.visit(ast, {
148 leave: {
149 Name: node => node.value,
150 Variable: node => `$${node.name}`,
151 // Document
152 Document: node => `${node.definitions
153 .map(defNode => `${defNode}\n${defNode[0] === '#' ? '' : '\n'}`)
154 .join('')
155 .trim()}\n`,
156 OperationTypeDefinition: node => `${node.operation}: ${node.type}`,
157 VariableDefinition: ({ variable, type, defaultValue }) => `${variable}: ${type}${wrap(' = ', defaultValue)}`,
158 SelectionSet: ({ selections }) => block(selections),
159 Field: ({ alias, name, arguments: args, directives, selectionSet }) => join([wrap('', alias, ': ') + name + wrap('(', join(args, ', '), ')'), join(directives, ' '), selectionSet], ' '),
160 Argument: addDescription(({ name, value }) => `${name}: ${value}`),
161 // Value
162 IntValue: ({ value }) => value,
163 FloatValue: ({ value }) => value,
164 StringValue: ({ value, block: isBlockString }, key) => isBlockString ? printBlockString(value, key === 'description') : JSON.stringify(value),
165 BooleanValue: ({ value }) => (value ? 'true' : 'false'),
166 NullValue: () => 'null',
167 EnumValue: ({ value }) => value,
168 ListValue: ({ values }) => `[${join(values, ', ')}]`,
169 ObjectValue: ({ fields }) => `{${join(fields, ', ')}}`,
170 ObjectField: ({ name, value }) => `${name}: ${value}`,
171 // Directive
172 Directive: ({ name, arguments: args }) => `@${name}${wrap('(', join(args, ', '), ')')}`,
173 // Type
174 NamedType: ({ name }) => name,
175 ListType: ({ type }) => `[${type}]`,
176 NonNullType: ({ type }) => `${type}!`,
177 // Type System Definitions
178 SchemaDefinition: ({ directives, operationTypes }) => join(['schema', join(directives, ' '), block(operationTypes)], ' '),
179 ScalarTypeDefinition: addDescription(({ name, directives }) => join(['scalar', name, join(directives, ' ')], ' ')),
180 ObjectTypeDefinition: addDescription(({ name, interfaces, directives, fields }) => join(['type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' ')),
181 FieldDefinition: addDescription(({ name, arguments: args, type, directives }) => `${name + wrap('(', join(args, ', '), ')')}: ${type}${wrap(' ', join(directives, ' '))}`),
182 InputValueDefinition: addDescription(({ name, type, defaultValue, directives }) => join([`${name}: ${type}`, wrap('= ', defaultValue), join(directives, ' ')], ' ')),
183 InterfaceTypeDefinition: addDescription(({ name, directives, fields }) => join(['interface', name, join(directives, ' '), block(fields)], ' ')),
184 UnionTypeDefinition: addDescription(({ name, directives, types }) => join(['union', name, join(directives, ' '), types && types.length !== 0 ? `= ${join(types, ' | ')}` : ''], ' ')),
185 EnumTypeDefinition: addDescription(({ name, directives, values }) => join(['enum', name, join(directives, ' '), block(values)], ' ')),
186 EnumValueDefinition: addDescription(({ name, directives }) => join([name, join(directives, ' ')], ' ')),
187 InputObjectTypeDefinition: addDescription(({ name, directives, fields }) => join(['input', name, join(directives, ' '), block(fields)], ' ')),
188 ScalarTypeExtension: ({ name, directives }) => join(['extend scalar', name, join(directives, ' ')], ' '),
189 ObjectTypeExtension: ({ name, interfaces, directives, fields }) => join(['extend type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '),
190 InterfaceTypeExtension: ({ name, directives, fields }) => join(['extend interface', name, join(directives, ' '), block(fields)], ' '),
191 UnionTypeExtension: ({ name, directives, types }) => join(['extend union', name, join(directives, ' '), types && types.length !== 0 ? `= ${join(types, ' | ')}` : ''], ' '),
192 EnumTypeExtension: ({ name, directives, values }) => join(['extend enum', name, join(directives, ' '), block(values)], ' '),
193 InputObjectTypeExtension: ({ name, directives, fields }) => join(['extend input', name, join(directives, ' '), block(fields)], ' '),
194 DirectiveDefinition: addDescription(({ name, arguments: args, locations }) => `directive @${name}${wrap('(', join(args, ', '), ')')} on ${join(locations, ' | ')}`),
195 },
196 });
197}
198function isFieldDefinitionNode(node) {
199 return node.kind === 'FieldDefinition';
200}
201
202function directiveAlreadyExists(directivesArr, otherDirective) {
203 return !!directivesArr.find(directive => directive.name.value === otherDirective.name.value);
204}
205function nameAlreadyExists(name, namesArr) {
206 return namesArr.some(({ value }) => value === name.value);
207}
208function mergeArguments$1(a1, a2) {
209 const result = [...a2];
210 for (const argument of a1) {
211 const existingIndex = result.findIndex(a => a.name.value === argument.name.value);
212 if (existingIndex > -1) {
213 const existingArg = result[existingIndex];
214 if (existingArg.value.kind === 'ListValue') {
215 const source = existingArg.value.values;
216 const target = argument.value.values;
217 // merge values of two lists
218 existingArg.value.values = deduplicateLists(source, target, (targetVal, source) => {
219 const value = targetVal.value;
220 return !value || !source.some((sourceVal) => sourceVal.value === value);
221 });
222 }
223 else {
224 existingArg.value = argument.value;
225 }
226 }
227 else {
228 result.push(argument);
229 }
230 }
231 return result;
232}
233function deduplicateDirectives(directives) {
234 return directives
235 .map((directive, i, all) => {
236 const firstAt = all.findIndex(d => d.name.value === directive.name.value);
237 if (firstAt !== i) {
238 const dup = all[firstAt];
239 directive.arguments = mergeArguments$1(directive.arguments, dup.arguments);
240 return null;
241 }
242 return directive;
243 })
244 .filter(d => d);
245}
246function mergeDirectives(d1 = [], d2 = [], config) {
247 const reverseOrder = config && config.reverseDirectives;
248 const asNext = reverseOrder ? d1 : d2;
249 const asFirst = reverseOrder ? d2 : d1;
250 const result = deduplicateDirectives([...asNext]);
251 for (const directive of asFirst) {
252 if (directiveAlreadyExists(result, directive)) {
253 const existingDirectiveIndex = result.findIndex(d => d.name.value === directive.name.value);
254 const existingDirective = result[existingDirectiveIndex];
255 result[existingDirectiveIndex].arguments = mergeArguments$1(directive.arguments || [], existingDirective.arguments || []);
256 }
257 else {
258 result.push(directive);
259 }
260 }
261 return result;
262}
263function validateInputs(node, existingNode) {
264 const printedNode = graphql.print(node);
265 const printedExistingNode = graphql.print(existingNode);
266 const leaveInputs = new RegExp('(directive @w*d*)|( on .*$)', 'g');
267 const sameArguments = printedNode.replace(leaveInputs, '') === printedExistingNode.replace(leaveInputs, '');
268 if (!sameArguments) {
269 throw new Error(`Unable to merge GraphQL directive "${node.name.value}". \nExisting directive: \n\t${printedExistingNode} \nReceived directive: \n\t${printedNode}`);
270 }
271}
272function mergeDirective(node, existingNode) {
273 if (existingNode) {
274 validateInputs(node, existingNode);
275 return {
276 ...node,
277 locations: [
278 ...existingNode.locations,
279 ...node.locations.filter(name => !nameAlreadyExists(name, existingNode.locations)),
280 ],
281 };
282 }
283 return node;
284}
285function deduplicateLists(source, target, filterFn) {
286 return source.concat(target.filter(val => filterFn(val, source)));
287}
288
289function mergeEnumValues(first, second, config) {
290 const enumValueMap = new Map();
291 for (const firstValue of first) {
292 enumValueMap.set(firstValue.name.value, firstValue);
293 }
294 for (const secondValue of second) {
295 const enumValue = secondValue.name.value;
296 if (enumValueMap.has(enumValue)) {
297 const firstValue = enumValueMap.get(enumValue);
298 firstValue.description = secondValue.description || firstValue.description;
299 firstValue.directives = mergeDirectives(secondValue.directives, firstValue.directives);
300 }
301 else {
302 enumValueMap.set(enumValue, secondValue);
303 }
304 }
305 const result = [...enumValueMap.values()];
306 if (config && config.sort) {
307 result.sort(utils.compareNodes);
308 }
309 return result;
310}
311
312function mergeEnum(e1, e2, config) {
313 if (e2) {
314 return {
315 name: e1.name,
316 description: e1['description'] || e2['description'],
317 kind: (config && config.convertExtensions) || e1.kind === 'EnumTypeDefinition' || e2.kind === 'EnumTypeDefinition'
318 ? 'EnumTypeDefinition'
319 : 'EnumTypeExtension',
320 loc: e1.loc,
321 directives: mergeDirectives(e1.directives, e2.directives, config),
322 values: mergeEnumValues(e1.values, e2.values, config),
323 };
324 }
325 return config && config.convertExtensions
326 ? {
327 ...e1,
328 kind: 'EnumTypeDefinition',
329 }
330 : e1;
331}
332
333function isStringTypes(types) {
334 return typeof types === 'string';
335}
336function isSourceTypes(types) {
337 return types instanceof graphql.Source;
338}
339function isGraphQLType(definition) {
340 return definition.kind === 'ObjectTypeDefinition';
341}
342function isGraphQLTypeExtension(definition) {
343 return definition.kind === 'ObjectTypeExtension';
344}
345function isGraphQLEnum(definition) {
346 return definition.kind === 'EnumTypeDefinition';
347}
348function isGraphQLEnumExtension(definition) {
349 return definition.kind === 'EnumTypeExtension';
350}
351function isGraphQLUnion(definition) {
352 return definition.kind === 'UnionTypeDefinition';
353}
354function isGraphQLUnionExtension(definition) {
355 return definition.kind === 'UnionTypeExtension';
356}
357function isGraphQLScalar(definition) {
358 return definition.kind === 'ScalarTypeDefinition';
359}
360function isGraphQLScalarExtension(definition) {
361 return definition.kind === 'ScalarTypeExtension';
362}
363function isGraphQLInputType(definition) {
364 return definition.kind === 'InputObjectTypeDefinition';
365}
366function isGraphQLInputTypeExtension(definition) {
367 return definition.kind === 'InputObjectTypeExtension';
368}
369function isGraphQLInterface(definition) {
370 return definition.kind === 'InterfaceTypeDefinition';
371}
372function isGraphQLInterfaceExtension(definition) {
373 return definition.kind === 'InterfaceTypeExtension';
374}
375function isGraphQLDirective(definition) {
376 return definition.kind === 'DirectiveDefinition';
377}
378function extractType(type) {
379 let visitedType = type;
380 while (visitedType.kind === 'ListType' || visitedType.kind === 'NonNullType') {
381 visitedType = visitedType.type;
382 }
383 return visitedType;
384}
385function isSchemaDefinition(node) {
386 return node.kind === 'SchemaDefinition';
387}
388function isWrappingTypeNode(type) {
389 return type.kind !== graphql.Kind.NAMED_TYPE;
390}
391function isListTypeNode(type) {
392 return type.kind === graphql.Kind.LIST_TYPE;
393}
394function isNonNullTypeNode(type) {
395 return type.kind === graphql.Kind.NON_NULL_TYPE;
396}
397function printTypeNode(type) {
398 if (isListTypeNode(type)) {
399 return `[${printTypeNode(type.type)}]`;
400 }
401 if (isNonNullTypeNode(type)) {
402 return `${printTypeNode(type.type)}!`;
403 }
404 return type.name.value;
405}
406
407function fieldAlreadyExists(fieldsArr, otherField) {
408 const result = fieldsArr.find(field => field.name.value === otherField.name.value);
409 if (result) {
410 const t1 = extractType(result.type);
411 const t2 = extractType(otherField.type);
412 if (t1.name.value !== t2.name.value) {
413 throw new Error(`Field "${otherField.name.value}" already defined with a different type. Declared as "${t1.name.value}", but you tried to override with "${t2.name.value}"`);
414 }
415 }
416 return !!result;
417}
418function mergeFields(type, f1, f2, config) {
419 const result = [...f2];
420 for (const field of f1) {
421 if (fieldAlreadyExists(result, field)) {
422 const existing = result.find((f) => f.name.value === field.name.value);
423 if (config && config.throwOnConflict) {
424 preventConflicts(type, existing, field, false);
425 }
426 else {
427 preventConflicts(type, existing, field, true);
428 }
429 if (isNonNullTypeNode(field.type) && !isNonNullTypeNode(existing.type)) {
430 existing.type = field.type;
431 }
432 existing.arguments = mergeArguments(field['arguments'] || [], existing.arguments || [], config);
433 existing.directives = mergeDirectives(field.directives, existing.directives, config);
434 existing.description = field.description || existing.description;
435 }
436 else {
437 result.push(field);
438 }
439 }
440 if (config && config.sort) {
441 result.sort(utils.compareNodes);
442 }
443 if (config && config.exclusions) {
444 return result.filter(field => !config.exclusions.includes(`${type.name.value}.${field.name.value}`));
445 }
446 return result;
447}
448function preventConflicts(type, a, b, ignoreNullability = false) {
449 const aType = printTypeNode(a.type);
450 const bType = printTypeNode(b.type);
451 if (utils.isNotEqual(aType, bType)) {
452 if (safeChangeForFieldType(a.type, b.type, ignoreNullability) === false) {
453 throw new Error(`Field '${type.name.value}.${a.name.value}' changed type from '${aType}' to '${bType}'`);
454 }
455 }
456}
457function safeChangeForFieldType(oldType, newType, ignoreNullability = false) {
458 // both are named
459 if (!isWrappingTypeNode(oldType) && !isWrappingTypeNode(newType)) {
460 return oldType.toString() === newType.toString();
461 }
462 // new is non-null
463 if (isNonNullTypeNode(newType)) {
464 const ofType = isNonNullTypeNode(oldType) ? oldType.type : oldType;
465 return safeChangeForFieldType(ofType, newType.type);
466 }
467 // old is non-null
468 if (isNonNullTypeNode(oldType)) {
469 return safeChangeForFieldType(newType, oldType, ignoreNullability);
470 }
471 // old is list
472 if (isListTypeNode(oldType)) {
473 return ((isListTypeNode(newType) && safeChangeForFieldType(oldType.type, newType.type)) ||
474 (isNonNullTypeNode(newType) && safeChangeForFieldType(oldType, newType['type'])));
475 }
476 return false;
477}
478
479function mergeInputType(node, existingNode, config) {
480 if (existingNode) {
481 try {
482 return {
483 name: node.name,
484 description: node['description'] || existingNode['description'],
485 kind: (config && config.convertExtensions) ||
486 node.kind === 'InputObjectTypeDefinition' ||
487 existingNode.kind === 'InputObjectTypeDefinition'
488 ? 'InputObjectTypeDefinition'
489 : 'InputObjectTypeExtension',
490 loc: node.loc,
491 fields: mergeFields(node, node.fields, existingNode.fields, config),
492 directives: mergeDirectives(node.directives, existingNode.directives, config),
493 };
494 }
495 catch (e) {
496 throw new Error(`Unable to merge GraphQL input type "${node.name.value}": ${e.message}`);
497 }
498 }
499 return config && config.convertExtensions
500 ? {
501 ...node,
502 kind: 'InputObjectTypeDefinition',
503 }
504 : node;
505}
506
507function mergeInterface(node, existingNode, config) {
508 if (existingNode) {
509 try {
510 return {
511 name: node.name,
512 description: node['description'] || existingNode['description'],
513 kind: (config && config.convertExtensions) ||
514 node.kind === 'InterfaceTypeDefinition' ||
515 existingNode.kind === 'InterfaceTypeDefinition'
516 ? 'InterfaceTypeDefinition'
517 : 'InterfaceTypeExtension',
518 loc: node.loc,
519 fields: mergeFields(node, node.fields, existingNode.fields, config),
520 directives: mergeDirectives(node.directives, existingNode.directives, config),
521 };
522 }
523 catch (e) {
524 throw new Error(`Unable to merge GraphQL interface "${node.name.value}": ${e.message}`);
525 }
526 }
527 return config && config.convertExtensions
528 ? {
529 ...node,
530 kind: 'InterfaceTypeDefinition',
531 }
532 : node;
533}
534
535function alreadyExists(arr, other) {
536 return !!arr.find(i => i.name.value === other.name.value);
537}
538function mergeNamedTypeArray(first, second, config) {
539 const result = [...second, ...first.filter(d => !alreadyExists(second, d))];
540 if (config && config.sort) {
541 result.sort(utils.compareNodes);
542 }
543 return result;
544}
545
546function mergeType(node, existingNode, config) {
547 if (existingNode) {
548 try {
549 return {
550 name: node.name,
551 description: node['description'] || existingNode['description'],
552 kind: (config && config.convertExtensions) ||
553 node.kind === 'ObjectTypeDefinition' ||
554 existingNode.kind === 'ObjectTypeDefinition'
555 ? 'ObjectTypeDefinition'
556 : 'ObjectTypeExtension',
557 loc: node.loc,
558 fields: mergeFields(node, node.fields, existingNode.fields, config),
559 directives: mergeDirectives(node.directives, existingNode.directives, config),
560 interfaces: mergeNamedTypeArray(node.interfaces, existingNode.interfaces, config),
561 };
562 }
563 catch (e) {
564 throw new Error(`Unable to merge GraphQL type "${node.name.value}": ${e.message}`);
565 }
566 }
567 return config && config.convertExtensions
568 ? {
569 ...node,
570 kind: 'ObjectTypeDefinition',
571 }
572 : node;
573}
574
575function mergeUnion(first, second, config) {
576 if (second) {
577 return {
578 name: first.name,
579 description: first['description'] || second['description'],
580 directives: mergeDirectives(first.directives, second.directives, config),
581 kind: (config && config.convertExtensions) ||
582 first.kind === 'UnionTypeDefinition' ||
583 second.kind === 'UnionTypeDefinition'
584 ? 'UnionTypeDefinition'
585 : 'UnionTypeExtension',
586 loc: first.loc,
587 types: mergeNamedTypeArray(first.types, second.types, config),
588 };
589 }
590 return config && config.convertExtensions
591 ? {
592 ...first,
593 kind: 'UnionTypeDefinition',
594 }
595 : first;
596}
597
598function mergeGraphQLNodes(nodes, config) {
599 return nodes.reduce((prev, nodeDefinition) => {
600 const node = nodeDefinition;
601 if (node && node.name && node.name.value) {
602 const name = node.name.value;
603 if (config && config.commentDescriptions) {
604 collectComment(node);
605 }
606 if (config &&
607 config.exclusions &&
608 (config.exclusions.includes(name + '.*') || config.exclusions.includes(name))) {
609 delete prev[name];
610 }
611 else if (isGraphQLType(nodeDefinition) || isGraphQLTypeExtension(nodeDefinition)) {
612 prev[name] = mergeType(nodeDefinition, prev[name], config);
613 }
614 else if (isGraphQLEnum(nodeDefinition) || isGraphQLEnumExtension(nodeDefinition)) {
615 prev[name] = mergeEnum(nodeDefinition, prev[name], config);
616 }
617 else if (isGraphQLUnion(nodeDefinition) || isGraphQLUnionExtension(nodeDefinition)) {
618 prev[name] = mergeUnion(nodeDefinition, prev[name], config);
619 }
620 else if (isGraphQLScalar(nodeDefinition) || isGraphQLScalarExtension(nodeDefinition)) {
621 prev[name] = nodeDefinition;
622 }
623 else if (isGraphQLInputType(nodeDefinition) || isGraphQLInputTypeExtension(nodeDefinition)) {
624 prev[name] = mergeInputType(nodeDefinition, prev[name], config);
625 }
626 else if (isGraphQLInterface(nodeDefinition) || isGraphQLInterfaceExtension(nodeDefinition)) {
627 prev[name] = mergeInterface(nodeDefinition, prev[name], config);
628 }
629 else if (isGraphQLDirective(nodeDefinition)) {
630 prev[name] = mergeDirective(nodeDefinition, prev[name]);
631 }
632 }
633 return prev;
634 }, {});
635}
636
637function mergeTypeDefs(types, config) {
638 resetComments();
639 const doc = {
640 kind: graphql.Kind.DOCUMENT,
641 definitions: mergeGraphQLTypes(types, {
642 useSchemaDefinition: true,
643 forceSchemaDefinition: false,
644 throwOnConflict: false,
645 commentDescriptions: false,
646 ...config,
647 }),
648 };
649 let result;
650 if (config && config.commentDescriptions) {
651 result = printWithComments(doc);
652 }
653 else {
654 result = doc;
655 }
656 resetComments();
657 return result;
658}
659function mergeGraphQLTypes(types, config) {
660 resetComments();
661 const allNodes = types
662 .map(type => {
663 if (Array.isArray(type)) {
664 type = mergeTypeDefs(type);
665 }
666 if (graphql.isSchema(type)) {
667 return graphql.parse(utils.printSchemaWithDirectives(type));
668 }
669 else if (isStringTypes(type) || isSourceTypes(type)) {
670 return graphql.parse(type);
671 }
672 return type;
673 })
674 .map(ast => ast.definitions)
675 .reduce((defs, newDef = []) => [...defs, ...newDef], []);
676 // XXX: right now we don't handle multiple schema definitions
677 let schemaDef = allNodes.filter(isSchemaDefinition).reduce((def, node) => {
678 node.operationTypes
679 .filter(op => op.type.name.value)
680 .forEach(op => {
681 def[op.operation] = op.type.name.value;
682 });
683 return def;
684 }, {
685 query: null,
686 mutation: null,
687 subscription: null,
688 });
689 const mergedNodes = mergeGraphQLNodes(allNodes, config);
690 const allTypes = Object.keys(mergedNodes);
691 if (config && config.sort) {
692 allTypes.sort(typeof config.sort === 'function' ? config.sort : undefined);
693 }
694 if (config && config.useSchemaDefinition) {
695 const queryType = schemaDef.query ? schemaDef.query : allTypes.find(t => t === 'Query');
696 const mutationType = schemaDef.mutation ? schemaDef.mutation : allTypes.find(t => t === 'Mutation');
697 const subscriptionType = schemaDef.subscription ? schemaDef.subscription : allTypes.find(t => t === 'Subscription');
698 schemaDef = {
699 query: queryType,
700 mutation: mutationType,
701 subscription: subscriptionType,
702 };
703 }
704 const schemaDefinition = utils.createSchemaDefinition(schemaDef, {
705 force: config.forceSchemaDefinition,
706 });
707 if (!schemaDefinition) {
708 return Object.values(mergedNodes);
709 }
710 return [...Object.values(mergedNodes), graphql.parse(schemaDefinition).definitions[0]];
711}
712
713const FILE_EXTENSIONS = ['.gql', '.gqls', '.graphql', '.graphqls'];
714function isGraphQLImportFile(rawSDL) {
715 const trimmedRawSDL = rawSDL.trim();
716 return trimmedRawSDL.startsWith('# import') || trimmedRawSDL.startsWith('#import');
717}
718/**
719 * This loader loads documents and type definitions from `.graphql` files.
720 *
721 * You can load a single source:
722 *
723 * ```js
724 * const schema = await loadSchema('schema.graphql', {
725 * loaders: [
726 * new GraphQLFileLoader()
727 * ]
728 * });
729 * ```
730 *
731 * Or provide a glob pattern to load multiple sources:
732 *
733 * ```js
734 * const schema = await loadSchema('graphql/*.graphql', {
735 * loaders: [
736 * new GraphQLFileLoader()
737 * ]
738 * });
739 * ```
740 */
741class GraphQLFileLoader {
742 loaderId() {
743 return 'graphql-file';
744 }
745 async canLoad(pointer, options) {
746 if (utils.isValidPath(pointer)) {
747 if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
748 const normalizedFilePath = path.isAbsolute(pointer) ? pointer : path.resolve(options.cwd || process.cwd(), pointer);
749 return fsExtra.pathExists(normalizedFilePath);
750 }
751 }
752 return false;
753 }
754 canLoadSync(pointer, options) {
755 if (utils.isValidPath(pointer)) {
756 if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
757 const normalizedFilePath = path.isAbsolute(pointer) ? pointer : path.resolve(options.cwd || process.cwd(), pointer);
758 return fsExtra.pathExistsSync(normalizedFilePath);
759 }
760 }
761 return false;
762 }
763 async load(pointer, options) {
764 const normalizedFilePath = path.isAbsolute(pointer) ? pointer : path.resolve(options.cwd || process.cwd(), pointer);
765 const rawSDL = await fsExtra.readFile(normalizedFilePath, { encoding: 'utf8' });
766 return this.handleFileContent(rawSDL, pointer, options);
767 }
768 loadSync(pointer, options) {
769 const normalizedFilePath = path.isAbsolute(pointer) ? pointer : path.resolve(options.cwd || process.cwd(), pointer);
770 const rawSDL = fsExtra.readFileSync(normalizedFilePath, { encoding: 'utf8' });
771 return this.handleFileContent(rawSDL, pointer, options);
772 }
773 handleFileContent(rawSDL, pointer, options) {
774 if (!options.skipGraphQLImport && isGraphQLImportFile(rawSDL)) {
775 const document = _import.processImport(pointer, options.cwd);
776 const typeSystemDefinitions = document.definitions
777 .filter(d => !graphql.isExecutableDefinitionNode(d))
778 .map(definition => ({
779 kind: graphql.Kind.DOCUMENT,
780 definitions: [definition],
781 }));
782 const mergedTypeDefs = mergeTypeDefs(typeSystemDefinitions, { useSchemaDefinition: false });
783 const executableDefinitions = document.definitions.filter(graphql.isExecutableDefinitionNode);
784 return {
785 location: pointer,
786 document: {
787 ...mergedTypeDefs,
788 definitions: [...mergedTypeDefs.definitions, ...executableDefinitions],
789 },
790 };
791 }
792 return utils.parseGraphQLSDL(pointer, rawSDL, options);
793 }
794}
795
796exports.GraphQLFileLoader = GraphQLFileLoader;
797//# sourceMappingURL=index.cjs.js.map