print-schema-with-directives.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. import { print, Kind, isSpecifiedScalarType, isIntrospectionType, isSpecifiedDirective, astFromValue, GraphQLDeprecatedDirective, isObjectType, isInterfaceType, isUnionType, isInputObjectType, isEnumType, isScalarType, } from 'graphql';
  2. import { astFromType } from './astFromType.js';
  3. import { getDirectivesInExtensions } from './get-directives.js';
  4. import { astFromValueUntyped } from './astFromValueUntyped.js';
  5. import { isSome } from './helpers.js';
  6. import { getRootTypeMap } from './rootTypes.js';
  7. export function getDocumentNodeFromSchema(schema, options = {}) {
  8. const pathToDirectivesInExtensions = options.pathToDirectivesInExtensions;
  9. const typesMap = schema.getTypeMap();
  10. const schemaNode = astFromSchema(schema, pathToDirectivesInExtensions);
  11. const definitions = schemaNode != null ? [schemaNode] : [];
  12. const directives = schema.getDirectives();
  13. for (const directive of directives) {
  14. if (isSpecifiedDirective(directive)) {
  15. continue;
  16. }
  17. definitions.push(astFromDirective(directive, schema, pathToDirectivesInExtensions));
  18. }
  19. for (const typeName in typesMap) {
  20. const type = typesMap[typeName];
  21. const isPredefinedScalar = isSpecifiedScalarType(type);
  22. const isIntrospection = isIntrospectionType(type);
  23. if (isPredefinedScalar || isIntrospection) {
  24. continue;
  25. }
  26. if (isObjectType(type)) {
  27. definitions.push(astFromObjectType(type, schema, pathToDirectivesInExtensions));
  28. }
  29. else if (isInterfaceType(type)) {
  30. definitions.push(astFromInterfaceType(type, schema, pathToDirectivesInExtensions));
  31. }
  32. else if (isUnionType(type)) {
  33. definitions.push(astFromUnionType(type, schema, pathToDirectivesInExtensions));
  34. }
  35. else if (isInputObjectType(type)) {
  36. definitions.push(astFromInputObjectType(type, schema, pathToDirectivesInExtensions));
  37. }
  38. else if (isEnumType(type)) {
  39. definitions.push(astFromEnumType(type, schema, pathToDirectivesInExtensions));
  40. }
  41. else if (isScalarType(type)) {
  42. definitions.push(astFromScalarType(type, schema, pathToDirectivesInExtensions));
  43. }
  44. else {
  45. throw new Error(`Unknown type ${type}.`);
  46. }
  47. }
  48. return {
  49. kind: Kind.DOCUMENT,
  50. definitions,
  51. };
  52. }
  53. // this approach uses the default schema printer rather than a custom solution, so may be more backwards compatible
  54. // currently does not allow customization of printSchema options having to do with comments.
  55. export function printSchemaWithDirectives(schema, options = {}) {
  56. const documentNode = getDocumentNodeFromSchema(schema, options);
  57. return print(documentNode);
  58. }
  59. export function astFromSchema(schema, pathToDirectivesInExtensions) {
  60. var _a, _b;
  61. const operationTypeMap = new Map([
  62. ['query', undefined],
  63. ['mutation', undefined],
  64. ['subscription', undefined],
  65. ]);
  66. const nodes = [];
  67. if (schema.astNode != null) {
  68. nodes.push(schema.astNode);
  69. }
  70. if (schema.extensionASTNodes != null) {
  71. for (const extensionASTNode of schema.extensionASTNodes) {
  72. nodes.push(extensionASTNode);
  73. }
  74. }
  75. for (const node of nodes) {
  76. if (node.operationTypes) {
  77. for (const operationTypeDefinitionNode of node.operationTypes) {
  78. operationTypeMap.set(operationTypeDefinitionNode.operation, operationTypeDefinitionNode);
  79. }
  80. }
  81. }
  82. const rootTypeMap = getRootTypeMap(schema);
  83. for (const [operationTypeNode, operationTypeDefinitionNode] of operationTypeMap) {
  84. const rootType = rootTypeMap.get(operationTypeNode);
  85. if (rootType != null) {
  86. const rootTypeAST = astFromType(rootType);
  87. if (operationTypeDefinitionNode != null) {
  88. operationTypeDefinitionNode.type = rootTypeAST;
  89. }
  90. else {
  91. operationTypeMap.set(operationTypeNode, {
  92. kind: Kind.OPERATION_TYPE_DEFINITION,
  93. operation: operationTypeNode,
  94. type: rootTypeAST,
  95. });
  96. }
  97. }
  98. }
  99. const operationTypes = [...operationTypeMap.values()].filter(isSome);
  100. const directives = getDirectiveNodes(schema, schema, pathToDirectivesInExtensions);
  101. if (!operationTypes.length && !directives.length) {
  102. return null;
  103. }
  104. const schemaNode = {
  105. kind: operationTypes != null ? Kind.SCHEMA_DEFINITION : Kind.SCHEMA_EXTENSION,
  106. operationTypes,
  107. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  108. directives: directives,
  109. };
  110. // This code is so weird because it needs to support GraphQL.js 14
  111. // In GraphQL.js 14 there is no `description` value on schemaNode
  112. schemaNode.description =
  113. ((_b = (_a = schema.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : schema.description != null)
  114. ? {
  115. kind: Kind.STRING,
  116. value: schema.description,
  117. block: true,
  118. }
  119. : undefined;
  120. return schemaNode;
  121. }
  122. export function astFromDirective(directive, schema, pathToDirectivesInExtensions) {
  123. var _a, _b, _c, _d;
  124. return {
  125. kind: Kind.DIRECTIVE_DEFINITION,
  126. description: (_b = (_a = directive.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (directive.description
  127. ? {
  128. kind: Kind.STRING,
  129. value: directive.description,
  130. }
  131. : undefined),
  132. name: {
  133. kind: Kind.NAME,
  134. value: directive.name,
  135. },
  136. arguments: (_c = directive.args) === null || _c === void 0 ? void 0 : _c.map(arg => astFromArg(arg, schema, pathToDirectivesInExtensions)),
  137. repeatable: directive.isRepeatable,
  138. locations: ((_d = directive.locations) === null || _d === void 0 ? void 0 : _d.map(location => ({
  139. kind: Kind.NAME,
  140. value: location,
  141. }))) || [],
  142. };
  143. }
  144. export function getDirectiveNodes(entity, schema, pathToDirectivesInExtensions) {
  145. const directivesInExtensions = getDirectivesInExtensions(entity, pathToDirectivesInExtensions);
  146. let nodes = [];
  147. if (entity.astNode != null) {
  148. nodes.push(entity.astNode);
  149. }
  150. if ('extensionASTNodes' in entity && entity.extensionASTNodes != null) {
  151. nodes = nodes.concat(entity.extensionASTNodes);
  152. }
  153. let directives;
  154. if (directivesInExtensions != null) {
  155. directives = makeDirectiveNodes(schema, directivesInExtensions);
  156. }
  157. else {
  158. directives = [];
  159. for (const node of nodes) {
  160. if (node.directives) {
  161. directives.push(...node.directives);
  162. }
  163. }
  164. }
  165. return directives;
  166. }
  167. export function getDeprecatableDirectiveNodes(entity, schema, pathToDirectivesInExtensions) {
  168. var _a, _b;
  169. let directiveNodesBesidesDeprecated = [];
  170. let deprecatedDirectiveNode = null;
  171. const directivesInExtensions = getDirectivesInExtensions(entity, pathToDirectivesInExtensions);
  172. let directives;
  173. if (directivesInExtensions != null) {
  174. directives = makeDirectiveNodes(schema, directivesInExtensions);
  175. }
  176. else {
  177. directives = (_a = entity.astNode) === null || _a === void 0 ? void 0 : _a.directives;
  178. }
  179. if (directives != null) {
  180. directiveNodesBesidesDeprecated = directives.filter(directive => directive.name.value !== 'deprecated');
  181. if (entity.deprecationReason != null) {
  182. deprecatedDirectiveNode = (_b = directives.filter(directive => directive.name.value === 'deprecated')) === null || _b === void 0 ? void 0 : _b[0];
  183. }
  184. }
  185. if (entity.deprecationReason != null &&
  186. deprecatedDirectiveNode == null) {
  187. deprecatedDirectiveNode = makeDeprecatedDirective(entity.deprecationReason);
  188. }
  189. return deprecatedDirectiveNode == null
  190. ? directiveNodesBesidesDeprecated
  191. : [deprecatedDirectiveNode].concat(directiveNodesBesidesDeprecated);
  192. }
  193. export function astFromArg(arg, schema, pathToDirectivesInExtensions) {
  194. var _a, _b, _c;
  195. return {
  196. kind: Kind.INPUT_VALUE_DEFINITION,
  197. description: (_b = (_a = arg.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (arg.description
  198. ? {
  199. kind: Kind.STRING,
  200. value: arg.description,
  201. block: true,
  202. }
  203. : undefined),
  204. name: {
  205. kind: Kind.NAME,
  206. value: arg.name,
  207. },
  208. type: astFromType(arg.type),
  209. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  210. defaultValue: arg.defaultValue !== undefined ? (_c = astFromValue(arg.defaultValue, arg.type)) !== null && _c !== void 0 ? _c : undefined : undefined,
  211. directives: getDeprecatableDirectiveNodes(arg, schema, pathToDirectivesInExtensions),
  212. };
  213. }
  214. export function astFromObjectType(type, schema, pathToDirectivesInExtensions) {
  215. var _a, _b;
  216. return {
  217. kind: Kind.OBJECT_TYPE_DEFINITION,
  218. description: (_b = (_a = type.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (type.description
  219. ? {
  220. kind: Kind.STRING,
  221. value: type.description,
  222. block: true,
  223. }
  224. : undefined),
  225. name: {
  226. kind: Kind.NAME,
  227. value: type.name,
  228. },
  229. fields: Object.values(type.getFields()).map(field => astFromField(field, schema, pathToDirectivesInExtensions)),
  230. interfaces: Object.values(type.getInterfaces()).map(iFace => astFromType(iFace)),
  231. directives: getDirectiveNodes(type, schema, pathToDirectivesInExtensions),
  232. };
  233. }
  234. export function astFromInterfaceType(type, schema, pathToDirectivesInExtensions) {
  235. var _a, _b;
  236. const node = {
  237. kind: Kind.INTERFACE_TYPE_DEFINITION,
  238. description: (_b = (_a = type.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (type.description
  239. ? {
  240. kind: Kind.STRING,
  241. value: type.description,
  242. block: true,
  243. }
  244. : undefined),
  245. name: {
  246. kind: Kind.NAME,
  247. value: type.name,
  248. },
  249. fields: Object.values(type.getFields()).map(field => astFromField(field, schema, pathToDirectivesInExtensions)),
  250. directives: getDirectiveNodes(type, schema, pathToDirectivesInExtensions),
  251. };
  252. if ('getInterfaces' in type) {
  253. node.interfaces = Object.values(type.getInterfaces()).map(iFace => astFromType(iFace));
  254. }
  255. return node;
  256. }
  257. export function astFromUnionType(type, schema, pathToDirectivesInExtensions) {
  258. var _a, _b;
  259. return {
  260. kind: Kind.UNION_TYPE_DEFINITION,
  261. description: (_b = (_a = type.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (type.description
  262. ? {
  263. kind: Kind.STRING,
  264. value: type.description,
  265. block: true,
  266. }
  267. : undefined),
  268. name: {
  269. kind: Kind.NAME,
  270. value: type.name,
  271. },
  272. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  273. directives: getDirectiveNodes(type, schema, pathToDirectivesInExtensions),
  274. types: type.getTypes().map(type => astFromType(type)),
  275. };
  276. }
  277. export function astFromInputObjectType(type, schema, pathToDirectivesInExtensions) {
  278. var _a, _b;
  279. return {
  280. kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
  281. description: (_b = (_a = type.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (type.description
  282. ? {
  283. kind: Kind.STRING,
  284. value: type.description,
  285. block: true,
  286. }
  287. : undefined),
  288. name: {
  289. kind: Kind.NAME,
  290. value: type.name,
  291. },
  292. fields: Object.values(type.getFields()).map(field => astFromInputField(field, schema, pathToDirectivesInExtensions)),
  293. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  294. directives: getDirectiveNodes(type, schema, pathToDirectivesInExtensions),
  295. };
  296. }
  297. export function astFromEnumType(type, schema, pathToDirectivesInExtensions) {
  298. var _a, _b;
  299. return {
  300. kind: Kind.ENUM_TYPE_DEFINITION,
  301. description: (_b = (_a = type.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (type.description
  302. ? {
  303. kind: Kind.STRING,
  304. value: type.description,
  305. block: true,
  306. }
  307. : undefined),
  308. name: {
  309. kind: Kind.NAME,
  310. value: type.name,
  311. },
  312. values: Object.values(type.getValues()).map(value => astFromEnumValue(value, schema, pathToDirectivesInExtensions)),
  313. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  314. directives: getDirectiveNodes(type, schema, pathToDirectivesInExtensions),
  315. };
  316. }
  317. export function astFromScalarType(type, schema, pathToDirectivesInExtensions) {
  318. var _a, _b, _c;
  319. const directivesInExtensions = getDirectivesInExtensions(type, pathToDirectivesInExtensions);
  320. const directives = directivesInExtensions
  321. ? makeDirectiveNodes(schema, directivesInExtensions)
  322. : ((_a = type.astNode) === null || _a === void 0 ? void 0 : _a.directives) || [];
  323. const specifiedByValue = (type['specifiedByUrl'] || type['specifiedByURL']);
  324. if (specifiedByValue && !directives.some(directiveNode => directiveNode.name.value === 'specifiedBy')) {
  325. const specifiedByArgs = {
  326. url: specifiedByValue,
  327. };
  328. directives.push(makeDirectiveNode('specifiedBy', specifiedByArgs));
  329. }
  330. return {
  331. kind: Kind.SCALAR_TYPE_DEFINITION,
  332. description: (_c = (_b = type.astNode) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : (type.description
  333. ? {
  334. kind: Kind.STRING,
  335. value: type.description,
  336. block: true,
  337. }
  338. : undefined),
  339. name: {
  340. kind: Kind.NAME,
  341. value: type.name,
  342. },
  343. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  344. directives: directives,
  345. };
  346. }
  347. export function astFromField(field, schema, pathToDirectivesInExtensions) {
  348. var _a, _b;
  349. return {
  350. kind: Kind.FIELD_DEFINITION,
  351. description: (_b = (_a = field.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (field.description
  352. ? {
  353. kind: Kind.STRING,
  354. value: field.description,
  355. block: true,
  356. }
  357. : undefined),
  358. name: {
  359. kind: Kind.NAME,
  360. value: field.name,
  361. },
  362. arguments: field.args.map(arg => astFromArg(arg, schema, pathToDirectivesInExtensions)),
  363. type: astFromType(field.type),
  364. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  365. directives: getDeprecatableDirectiveNodes(field, schema, pathToDirectivesInExtensions),
  366. };
  367. }
  368. export function astFromInputField(field, schema, pathToDirectivesInExtensions) {
  369. var _a, _b, _c;
  370. return {
  371. kind: Kind.INPUT_VALUE_DEFINITION,
  372. description: (_b = (_a = field.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (field.description
  373. ? {
  374. kind: Kind.STRING,
  375. value: field.description,
  376. block: true,
  377. }
  378. : undefined),
  379. name: {
  380. kind: Kind.NAME,
  381. value: field.name,
  382. },
  383. type: astFromType(field.type),
  384. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  385. directives: getDeprecatableDirectiveNodes(field, schema, pathToDirectivesInExtensions),
  386. defaultValue: (_c = astFromValue(field.defaultValue, field.type)) !== null && _c !== void 0 ? _c : undefined,
  387. };
  388. }
  389. export function astFromEnumValue(value, schema, pathToDirectivesInExtensions) {
  390. var _a, _b;
  391. return {
  392. kind: Kind.ENUM_VALUE_DEFINITION,
  393. description: (_b = (_a = value.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : (value.description
  394. ? {
  395. kind: Kind.STRING,
  396. value: value.description,
  397. block: true,
  398. }
  399. : undefined),
  400. name: {
  401. kind: Kind.NAME,
  402. value: value.name,
  403. },
  404. // ConstXNode has been introduced in v16 but it is not compatible with XNode so we do `as any` for backwards compatibility
  405. directives: getDeprecatableDirectiveNodes(value, schema, pathToDirectivesInExtensions),
  406. };
  407. }
  408. export function makeDeprecatedDirective(deprecationReason) {
  409. return makeDirectiveNode('deprecated', { reason: deprecationReason }, GraphQLDeprecatedDirective);
  410. }
  411. export function makeDirectiveNode(name, args, directive) {
  412. const directiveArguments = [];
  413. if (directive != null) {
  414. for (const arg of directive.args) {
  415. const argName = arg.name;
  416. const argValue = args[argName];
  417. if (argValue !== undefined) {
  418. const value = astFromValue(argValue, arg.type);
  419. if (value) {
  420. directiveArguments.push({
  421. kind: Kind.ARGUMENT,
  422. name: {
  423. kind: Kind.NAME,
  424. value: argName,
  425. },
  426. value,
  427. });
  428. }
  429. }
  430. }
  431. }
  432. else {
  433. for (const argName in args) {
  434. const argValue = args[argName];
  435. const value = astFromValueUntyped(argValue);
  436. if (value) {
  437. directiveArguments.push({
  438. kind: Kind.ARGUMENT,
  439. name: {
  440. kind: Kind.NAME,
  441. value: argName,
  442. },
  443. value,
  444. });
  445. }
  446. }
  447. }
  448. return {
  449. kind: Kind.DIRECTIVE,
  450. name: {
  451. kind: Kind.NAME,
  452. value: name,
  453. },
  454. arguments: directiveArguments,
  455. };
  456. }
  457. export function makeDirectiveNodes(schema, directiveValues) {
  458. const directiveNodes = [];
  459. for (const directiveName in directiveValues) {
  460. const arrayOrSingleValue = directiveValues[directiveName];
  461. const directive = schema === null || schema === void 0 ? void 0 : schema.getDirective(directiveName);
  462. if (Array.isArray(arrayOrSingleValue)) {
  463. for (const value of arrayOrSingleValue) {
  464. directiveNodes.push(makeDirectiveNode(directiveName, value, directive));
  465. }
  466. }
  467. else {
  468. directiveNodes.push(makeDirectiveNode(directiveName, arrayOrSingleValue, directive));
  469. }
  470. }
  471. return directiveNodes;
  472. }