lexicographicSortSchema.js.flow 4.5 KB

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