lexicographicSortSchema.mjs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { inspect } from '../jsutils/inspect.mjs';
  2. import { invariant } from '../jsutils/invariant.mjs';
  3. import { keyValMap } from '../jsutils/keyValMap.mjs';
  4. import { naturalCompare } from '../jsutils/naturalCompare.mjs';
  5. import {
  6. GraphQLEnumType,
  7. GraphQLInputObjectType,
  8. GraphQLInterfaceType,
  9. GraphQLList,
  10. GraphQLNonNull,
  11. GraphQLObjectType,
  12. GraphQLUnionType,
  13. isEnumType,
  14. isInputObjectType,
  15. isInterfaceType,
  16. isListType,
  17. isNonNullType,
  18. isObjectType,
  19. isScalarType,
  20. isUnionType,
  21. } from '../type/definition.mjs';
  22. import { GraphQLDirective } from '../type/directives.mjs';
  23. import { isIntrospectionType } from '../type/introspection.mjs';
  24. import { GraphQLSchema } from '../type/schema.mjs';
  25. /**
  26. * Sort GraphQLSchema.
  27. *
  28. * This function returns a sorted copy of the given GraphQLSchema.
  29. */
  30. export function lexicographicSortSchema(schema) {
  31. const schemaConfig = schema.toConfig();
  32. const typeMap = keyValMap(
  33. sortByName(schemaConfig.types),
  34. (type) => type.name,
  35. sortNamedType,
  36. );
  37. return new GraphQLSchema({
  38. ...schemaConfig,
  39. types: Object.values(typeMap),
  40. directives: sortByName(schemaConfig.directives).map(sortDirective),
  41. query: replaceMaybeType(schemaConfig.query),
  42. mutation: replaceMaybeType(schemaConfig.mutation),
  43. subscription: replaceMaybeType(schemaConfig.subscription),
  44. });
  45. function replaceType(type) {
  46. if (isListType(type)) {
  47. // @ts-expect-error
  48. return new GraphQLList(replaceType(type.ofType));
  49. } else if (isNonNullType(type)) {
  50. // @ts-expect-error
  51. return new GraphQLNonNull(replaceType(type.ofType));
  52. } // @ts-expect-error FIXME: TS Conversion
  53. return replaceNamedType(type);
  54. }
  55. function replaceNamedType(type) {
  56. return typeMap[type.name];
  57. }
  58. function replaceMaybeType(maybeType) {
  59. return maybeType && replaceNamedType(maybeType);
  60. }
  61. function sortDirective(directive) {
  62. const config = directive.toConfig();
  63. return new GraphQLDirective({
  64. ...config,
  65. locations: sortBy(config.locations, (x) => x),
  66. args: sortArgs(config.args),
  67. });
  68. }
  69. function sortArgs(args) {
  70. return sortObjMap(args, (arg) => ({ ...arg, type: replaceType(arg.type) }));
  71. }
  72. function sortFields(fieldsMap) {
  73. return sortObjMap(fieldsMap, (field) => ({
  74. ...field,
  75. type: replaceType(field.type),
  76. args: field.args && sortArgs(field.args),
  77. }));
  78. }
  79. function sortInputFields(fieldsMap) {
  80. return sortObjMap(fieldsMap, (field) => ({
  81. ...field,
  82. type: replaceType(field.type),
  83. }));
  84. }
  85. function sortTypes(array) {
  86. return sortByName(array).map(replaceNamedType);
  87. }
  88. function sortNamedType(type) {
  89. if (isScalarType(type) || isIntrospectionType(type)) {
  90. return type;
  91. }
  92. if (isObjectType(type)) {
  93. const config = type.toConfig();
  94. return new GraphQLObjectType({
  95. ...config,
  96. interfaces: () => sortTypes(config.interfaces),
  97. fields: () => sortFields(config.fields),
  98. });
  99. }
  100. if (isInterfaceType(type)) {
  101. const config = type.toConfig();
  102. return new GraphQLInterfaceType({
  103. ...config,
  104. interfaces: () => sortTypes(config.interfaces),
  105. fields: () => sortFields(config.fields),
  106. });
  107. }
  108. if (isUnionType(type)) {
  109. const config = type.toConfig();
  110. return new GraphQLUnionType({
  111. ...config,
  112. types: () => sortTypes(config.types),
  113. });
  114. }
  115. if (isEnumType(type)) {
  116. const config = type.toConfig();
  117. return new GraphQLEnumType({
  118. ...config,
  119. values: sortObjMap(config.values, (value) => value),
  120. });
  121. }
  122. if (isInputObjectType(type)) {
  123. const config = type.toConfig();
  124. return new GraphQLInputObjectType({
  125. ...config,
  126. fields: () => sortInputFields(config.fields),
  127. });
  128. }
  129. /* c8 ignore next 3 */
  130. // Not reachable, all possible types have been considered.
  131. false || invariant(false, 'Unexpected type: ' + inspect(type));
  132. }
  133. }
  134. function sortObjMap(map, sortValueFn) {
  135. const sortedMap = Object.create(null);
  136. for (const key of Object.keys(map).sort(naturalCompare)) {
  137. sortedMap[key] = sortValueFn(map[key]);
  138. }
  139. return sortedMap;
  140. }
  141. function sortByName(array) {
  142. return sortBy(array, (obj) => obj.name);
  143. }
  144. function sortBy(array, mapToKey) {
  145. return array.slice().sort((obj1, obj2) => {
  146. const key1 = mapToKey(obj1);
  147. const key2 = mapToKey(obj2);
  148. return naturalCompare(key1, key2);
  149. });
  150. }