extendSchema.js.flow 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. // @flow strict
  2. import objectValues from '../polyfills/objectValues';
  3. import keyMap from '../jsutils/keyMap';
  4. import inspect from '../jsutils/inspect';
  5. import mapValue from '../jsutils/mapValue';
  6. import invariant from '../jsutils/invariant';
  7. import devAssert from '../jsutils/devAssert';
  8. import type { DirectiveLocationEnum } from '../language/directiveLocation';
  9. import type {
  10. Location,
  11. DocumentNode,
  12. StringValueNode,
  13. TypeNode,
  14. NamedTypeNode,
  15. SchemaDefinitionNode,
  16. SchemaExtensionNode,
  17. TypeDefinitionNode,
  18. InterfaceTypeDefinitionNode,
  19. InterfaceTypeExtensionNode,
  20. ObjectTypeDefinitionNode,
  21. ObjectTypeExtensionNode,
  22. UnionTypeDefinitionNode,
  23. UnionTypeExtensionNode,
  24. FieldDefinitionNode,
  25. InputObjectTypeDefinitionNode,
  26. InputObjectTypeExtensionNode,
  27. InputValueDefinitionNode,
  28. EnumTypeDefinitionNode,
  29. EnumTypeExtensionNode,
  30. EnumValueDefinitionNode,
  31. DirectiveDefinitionNode,
  32. ScalarTypeDefinitionNode,
  33. ScalarTypeExtensionNode,
  34. } from '../language/ast';
  35. import { Kind } from '../language/kinds';
  36. import { TokenKind } from '../language/tokenKind';
  37. import { dedentBlockStringValue } from '../language/blockString';
  38. import {
  39. isTypeDefinitionNode,
  40. isTypeExtensionNode,
  41. } from '../language/predicates';
  42. import { assertValidSDLExtension } from '../validation/validate';
  43. import { getDirectiveValues } from '../execution/values';
  44. import type {
  45. GraphQLSchemaValidationOptions,
  46. GraphQLSchemaNormalizedConfig,
  47. } from '../type/schema';
  48. import type {
  49. GraphQLType,
  50. GraphQLNamedType,
  51. GraphQLFieldConfig,
  52. GraphQLFieldConfigMap,
  53. GraphQLArgumentConfig,
  54. GraphQLFieldConfigArgumentMap,
  55. GraphQLEnumValueConfigMap,
  56. GraphQLInputFieldConfigMap,
  57. } from '../type/definition';
  58. import { assertSchema, GraphQLSchema } from '../type/schema';
  59. import { specifiedScalarTypes, isSpecifiedScalarType } from '../type/scalars';
  60. import { introspectionTypes, isIntrospectionType } from '../type/introspection';
  61. import {
  62. GraphQLDirective,
  63. GraphQLDeprecatedDirective,
  64. GraphQLSpecifiedByDirective,
  65. } from '../type/directives';
  66. import {
  67. isScalarType,
  68. isObjectType,
  69. isInterfaceType,
  70. isUnionType,
  71. isListType,
  72. isNonNullType,
  73. isEnumType,
  74. isInputObjectType,
  75. GraphQLList,
  76. GraphQLNonNull,
  77. GraphQLScalarType,
  78. GraphQLObjectType,
  79. GraphQLInterfaceType,
  80. GraphQLUnionType,
  81. GraphQLEnumType,
  82. GraphQLInputObjectType,
  83. } from '../type/definition';
  84. import { valueFromAST } from './valueFromAST';
  85. type Options = {|
  86. ...GraphQLSchemaValidationOptions,
  87. /**
  88. * Descriptions are defined as preceding string literals, however an older
  89. * experimental version of the SDL supported preceding comments as
  90. * descriptions. Set to true to enable this deprecated behavior.
  91. * This option is provided to ease adoption and will be removed in v16.
  92. *
  93. * Default: false
  94. */
  95. commentDescriptions?: boolean,
  96. /**
  97. * Set to true to assume the SDL is valid.
  98. *
  99. * Default: false
  100. */
  101. assumeValidSDL?: boolean,
  102. |};
  103. /**
  104. * Produces a new schema given an existing schema and a document which may
  105. * contain GraphQL type extensions and definitions. The original schema will
  106. * remain unaltered.
  107. *
  108. * Because a schema represents a graph of references, a schema cannot be
  109. * extended without effectively making an entire copy. We do not know until it's
  110. * too late if subgraphs remain unchanged.
  111. *
  112. * This algorithm copies the provided schema, applying extensions while
  113. * producing the copy. The original schema remains unaltered.
  114. *
  115. * Accepts options as a third argument:
  116. *
  117. * - commentDescriptions:
  118. * Provide true to use preceding comments as the description.
  119. *
  120. */
  121. export function extendSchema(
  122. schema: GraphQLSchema,
  123. documentAST: DocumentNode,
  124. options?: Options,
  125. ): GraphQLSchema {
  126. assertSchema(schema);
  127. devAssert(
  128. documentAST != null && documentAST.kind === Kind.DOCUMENT,
  129. 'Must provide valid Document AST.',
  130. );
  131. if (options?.assumeValid !== true && options?.assumeValidSDL !== true) {
  132. assertValidSDLExtension(documentAST, schema);
  133. }
  134. const schemaConfig = schema.toConfig();
  135. const extendedConfig = extendSchemaImpl(schemaConfig, documentAST, options);
  136. return schemaConfig === extendedConfig
  137. ? schema
  138. : new GraphQLSchema(extendedConfig);
  139. }
  140. /**
  141. * @internal
  142. */
  143. export function extendSchemaImpl(
  144. schemaConfig: GraphQLSchemaNormalizedConfig,
  145. documentAST: DocumentNode,
  146. options?: Options,
  147. ): GraphQLSchemaNormalizedConfig {
  148. // Collect the type definitions and extensions found in the document.
  149. const typeDefs: Array<TypeDefinitionNode> = [];
  150. const typeExtensionsMap = Object.create(null);
  151. // New directives and types are separate because a directives and types can
  152. // have the same name. For example, a type named "skip".
  153. const directiveDefs: Array<DirectiveDefinitionNode> = [];
  154. let schemaDef: ?SchemaDefinitionNode;
  155. // Schema extensions are collected which may add additional operation types.
  156. const schemaExtensions: Array<SchemaExtensionNode> = [];
  157. for (const def of documentAST.definitions) {
  158. if (def.kind === Kind.SCHEMA_DEFINITION) {
  159. schemaDef = def;
  160. } else if (def.kind === Kind.SCHEMA_EXTENSION) {
  161. schemaExtensions.push(def);
  162. } else if (isTypeDefinitionNode(def)) {
  163. typeDefs.push(def);
  164. } else if (isTypeExtensionNode(def)) {
  165. const extendedTypeName = def.name.value;
  166. const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
  167. typeExtensionsMap[extendedTypeName] = existingTypeExtensions
  168. ? existingTypeExtensions.concat([def])
  169. : [def];
  170. } else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
  171. directiveDefs.push(def);
  172. }
  173. }
  174. // If this document contains no new types, extensions, or directives then
  175. // return the same unmodified GraphQLSchema instance.
  176. if (
  177. Object.keys(typeExtensionsMap).length === 0 &&
  178. typeDefs.length === 0 &&
  179. directiveDefs.length === 0 &&
  180. schemaExtensions.length === 0 &&
  181. schemaDef == null
  182. ) {
  183. return schemaConfig;
  184. }
  185. const typeMap = Object.create(null);
  186. for (const existingType of schemaConfig.types) {
  187. typeMap[existingType.name] = extendNamedType(existingType);
  188. }
  189. for (const typeNode of typeDefs) {
  190. const name = typeNode.name.value;
  191. typeMap[name] = stdTypeMap[name] ?? buildType(typeNode);
  192. }
  193. const operationTypes = {
  194. // Get the extended root operation types.
  195. query: schemaConfig.query && replaceNamedType(schemaConfig.query),
  196. mutation: schemaConfig.mutation && replaceNamedType(schemaConfig.mutation),
  197. subscription:
  198. schemaConfig.subscription && replaceNamedType(schemaConfig.subscription),
  199. // Then, incorporate schema definition and all schema extensions.
  200. ...(schemaDef && getOperationTypes([schemaDef])),
  201. ...getOperationTypes(schemaExtensions),
  202. };
  203. // Then produce and return a Schema config with these types.
  204. return {
  205. description: schemaDef?.description?.value,
  206. ...operationTypes,
  207. types: objectValues(typeMap),
  208. directives: [
  209. ...schemaConfig.directives.map(replaceDirective),
  210. ...directiveDefs.map(buildDirective),
  211. ],
  212. extensions: undefined,
  213. astNode: schemaDef ?? schemaConfig.astNode,
  214. extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions),
  215. assumeValid: options?.assumeValid ?? false,
  216. };
  217. // Below are functions used for producing this schema that have closed over
  218. // this scope and have access to the schema, cache, and newly defined types.
  219. function replaceType<T: GraphQLType>(type: T): T {
  220. if (isListType(type)) {
  221. // $FlowFixMe[incompatible-return]
  222. return new GraphQLList(replaceType(type.ofType));
  223. }
  224. if (isNonNullType(type)) {
  225. // $FlowFixMe[incompatible-return]
  226. return new GraphQLNonNull(replaceType(type.ofType));
  227. }
  228. return replaceNamedType(type);
  229. }
  230. function replaceNamedType<T: GraphQLNamedType>(type: T): T {
  231. // Note: While this could make early assertions to get the correctly
  232. // typed values, that would throw immediately while type system
  233. // validation with validateSchema() will produce more actionable results.
  234. return ((typeMap[type.name]: any): T);
  235. }
  236. function replaceDirective(directive: GraphQLDirective): GraphQLDirective {
  237. const config = directive.toConfig();
  238. return new GraphQLDirective({
  239. ...config,
  240. args: mapValue(config.args, extendArg),
  241. });
  242. }
  243. function extendNamedType(type: GraphQLNamedType): GraphQLNamedType {
  244. if (isIntrospectionType(type) || isSpecifiedScalarType(type)) {
  245. // Builtin types are not extended.
  246. return type;
  247. }
  248. if (isScalarType(type)) {
  249. return extendScalarType(type);
  250. }
  251. if (isObjectType(type)) {
  252. return extendObjectType(type);
  253. }
  254. if (isInterfaceType(type)) {
  255. return extendInterfaceType(type);
  256. }
  257. if (isUnionType(type)) {
  258. return extendUnionType(type);
  259. }
  260. if (isEnumType(type)) {
  261. return extendEnumType(type);
  262. }
  263. // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
  264. if (isInputObjectType(type)) {
  265. return extendInputObjectType(type);
  266. }
  267. // istanbul ignore next (Not reachable. All possible types have been considered)
  268. invariant(false, 'Unexpected type: ' + inspect((type: empty)));
  269. }
  270. function extendInputObjectType(
  271. type: GraphQLInputObjectType,
  272. ): GraphQLInputObjectType {
  273. const config = type.toConfig();
  274. const extensions = typeExtensionsMap[config.name] ?? [];
  275. return new GraphQLInputObjectType({
  276. ...config,
  277. fields: () => ({
  278. ...mapValue(config.fields, (field) => ({
  279. ...field,
  280. type: replaceType(field.type),
  281. })),
  282. ...buildInputFieldMap(extensions),
  283. }),
  284. extensionASTNodes: config.extensionASTNodes.concat(extensions),
  285. });
  286. }
  287. function extendEnumType(type: GraphQLEnumType): GraphQLEnumType {
  288. const config = type.toConfig();
  289. const extensions = typeExtensionsMap[type.name] ?? [];
  290. return new GraphQLEnumType({
  291. ...config,
  292. values: {
  293. ...config.values,
  294. ...buildEnumValueMap(extensions),
  295. },
  296. extensionASTNodes: config.extensionASTNodes.concat(extensions),
  297. });
  298. }
  299. function extendScalarType(type: GraphQLScalarType): GraphQLScalarType {
  300. const config = type.toConfig();
  301. const extensions = typeExtensionsMap[config.name] ?? [];
  302. let specifiedByUrl = config.specifiedByUrl;
  303. for (const extensionNode of extensions) {
  304. specifiedByUrl = getSpecifiedByUrl(extensionNode) ?? specifiedByUrl;
  305. }
  306. return new GraphQLScalarType({
  307. ...config,
  308. specifiedByUrl,
  309. extensionASTNodes: config.extensionASTNodes.concat(extensions),
  310. });
  311. }
  312. function extendObjectType(type: GraphQLObjectType): GraphQLObjectType {
  313. const config = type.toConfig();
  314. const extensions = typeExtensionsMap[config.name] ?? [];
  315. return new GraphQLObjectType({
  316. ...config,
  317. interfaces: () => [
  318. ...type.getInterfaces().map(replaceNamedType),
  319. ...buildInterfaces(extensions),
  320. ],
  321. fields: () => ({
  322. ...mapValue(config.fields, extendField),
  323. ...buildFieldMap(extensions),
  324. }),
  325. extensionASTNodes: config.extensionASTNodes.concat(extensions),
  326. });
  327. }
  328. function extendInterfaceType(
  329. type: GraphQLInterfaceType,
  330. ): GraphQLInterfaceType {
  331. const config = type.toConfig();
  332. const extensions = typeExtensionsMap[config.name] ?? [];
  333. return new GraphQLInterfaceType({
  334. ...config,
  335. interfaces: () => [
  336. ...type.getInterfaces().map(replaceNamedType),
  337. ...buildInterfaces(extensions),
  338. ],
  339. fields: () => ({
  340. ...mapValue(config.fields, extendField),
  341. ...buildFieldMap(extensions),
  342. }),
  343. extensionASTNodes: config.extensionASTNodes.concat(extensions),
  344. });
  345. }
  346. function extendUnionType(type: GraphQLUnionType): GraphQLUnionType {
  347. const config = type.toConfig();
  348. const extensions = typeExtensionsMap[config.name] ?? [];
  349. return new GraphQLUnionType({
  350. ...config,
  351. types: () => [
  352. ...type.getTypes().map(replaceNamedType),
  353. ...buildUnionTypes(extensions),
  354. ],
  355. extensionASTNodes: config.extensionASTNodes.concat(extensions),
  356. });
  357. }
  358. function extendField(
  359. field: GraphQLFieldConfig<mixed, mixed>,
  360. ): GraphQLFieldConfig<mixed, mixed> {
  361. return {
  362. ...field,
  363. type: replaceType(field.type),
  364. // $FlowFixMe[incompatible-call]
  365. args: mapValue(field.args, extendArg),
  366. };
  367. }
  368. function extendArg(arg: GraphQLArgumentConfig) {
  369. return {
  370. ...arg,
  371. type: replaceType(arg.type),
  372. };
  373. }
  374. function getOperationTypes(
  375. nodes: $ReadOnlyArray<SchemaDefinitionNode | SchemaExtensionNode>,
  376. ): {|
  377. query: ?GraphQLObjectType,
  378. mutation: ?GraphQLObjectType,
  379. subscription: ?GraphQLObjectType,
  380. |} {
  381. const opTypes = {};
  382. for (const node of nodes) {
  383. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  384. const operationTypesNodes = node.operationTypes ?? [];
  385. for (const operationType of operationTypesNodes) {
  386. opTypes[operationType.operation] = getNamedType(operationType.type);
  387. }
  388. }
  389. // Note: While this could make early assertions to get the correctly
  390. // typed values below, that would throw immediately while type system
  391. // validation with validateSchema() will produce more actionable results.
  392. return (opTypes: any);
  393. }
  394. function getNamedType(node: NamedTypeNode): GraphQLNamedType {
  395. const name = node.name.value;
  396. const type = stdTypeMap[name] ?? typeMap[name];
  397. if (type === undefined) {
  398. throw new Error(`Unknown type: "${name}".`);
  399. }
  400. return type;
  401. }
  402. function getWrappedType(node: TypeNode): GraphQLType {
  403. if (node.kind === Kind.LIST_TYPE) {
  404. return new GraphQLList(getWrappedType(node.type));
  405. }
  406. if (node.kind === Kind.NON_NULL_TYPE) {
  407. return new GraphQLNonNull(getWrappedType(node.type));
  408. }
  409. return getNamedType(node);
  410. }
  411. function buildDirective(node: DirectiveDefinitionNode): GraphQLDirective {
  412. const locations = node.locations.map(
  413. ({ value }) => ((value: any): DirectiveLocationEnum),
  414. );
  415. return new GraphQLDirective({
  416. name: node.name.value,
  417. description: getDescription(node, options),
  418. locations,
  419. isRepeatable: node.repeatable,
  420. args: buildArgumentMap(node.arguments),
  421. astNode: node,
  422. });
  423. }
  424. function buildFieldMap(
  425. nodes: $ReadOnlyArray<
  426. | InterfaceTypeDefinitionNode
  427. | InterfaceTypeExtensionNode
  428. | ObjectTypeDefinitionNode
  429. | ObjectTypeExtensionNode,
  430. >,
  431. ): GraphQLFieldConfigMap<mixed, mixed> {
  432. const fieldConfigMap = Object.create(null);
  433. for (const node of nodes) {
  434. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  435. const nodeFields = node.fields ?? [];
  436. for (const field of nodeFields) {
  437. fieldConfigMap[field.name.value] = {
  438. // Note: While this could make assertions to get the correctly typed
  439. // value, that would throw immediately while type system validation
  440. // with validateSchema() will produce more actionable results.
  441. type: (getWrappedType(field.type): any),
  442. description: getDescription(field, options),
  443. args: buildArgumentMap(field.arguments),
  444. deprecationReason: getDeprecationReason(field),
  445. astNode: field,
  446. };
  447. }
  448. }
  449. return fieldConfigMap;
  450. }
  451. function buildArgumentMap(
  452. args: ?$ReadOnlyArray<InputValueDefinitionNode>,
  453. ): GraphQLFieldConfigArgumentMap {
  454. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  455. const argsNodes = args ?? [];
  456. const argConfigMap = Object.create(null);
  457. for (const arg of argsNodes) {
  458. // Note: While this could make assertions to get the correctly typed
  459. // value, that would throw immediately while type system validation
  460. // with validateSchema() will produce more actionable results.
  461. const type: any = getWrappedType(arg.type);
  462. argConfigMap[arg.name.value] = {
  463. type,
  464. description: getDescription(arg, options),
  465. defaultValue: valueFromAST(arg.defaultValue, type),
  466. deprecationReason: getDeprecationReason(arg),
  467. astNode: arg,
  468. };
  469. }
  470. return argConfigMap;
  471. }
  472. function buildInputFieldMap(
  473. nodes: $ReadOnlyArray<
  474. InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode,
  475. >,
  476. ): GraphQLInputFieldConfigMap {
  477. const inputFieldMap = Object.create(null);
  478. for (const node of nodes) {
  479. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  480. const fieldsNodes = node.fields ?? [];
  481. for (const field of fieldsNodes) {
  482. // Note: While this could make assertions to get the correctly typed
  483. // value, that would throw immediately while type system validation
  484. // with validateSchema() will produce more actionable results.
  485. const type: any = getWrappedType(field.type);
  486. inputFieldMap[field.name.value] = {
  487. type,
  488. description: getDescription(field, options),
  489. defaultValue: valueFromAST(field.defaultValue, type),
  490. deprecationReason: getDeprecationReason(field),
  491. astNode: field,
  492. };
  493. }
  494. }
  495. return inputFieldMap;
  496. }
  497. function buildEnumValueMap(
  498. nodes: $ReadOnlyArray<EnumTypeDefinitionNode | EnumTypeExtensionNode>,
  499. ): GraphQLEnumValueConfigMap {
  500. const enumValueMap = Object.create(null);
  501. for (const node of nodes) {
  502. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  503. const valuesNodes = node.values ?? [];
  504. for (const value of valuesNodes) {
  505. enumValueMap[value.name.value] = {
  506. description: getDescription(value, options),
  507. deprecationReason: getDeprecationReason(value),
  508. astNode: value,
  509. };
  510. }
  511. }
  512. return enumValueMap;
  513. }
  514. function buildInterfaces(
  515. nodes: $ReadOnlyArray<
  516. | InterfaceTypeDefinitionNode
  517. | InterfaceTypeExtensionNode
  518. | ObjectTypeDefinitionNode
  519. | ObjectTypeExtensionNode,
  520. >,
  521. ): Array<GraphQLInterfaceType> {
  522. const interfaces = [];
  523. for (const node of nodes) {
  524. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  525. const interfacesNodes = node.interfaces ?? [];
  526. for (const type of interfacesNodes) {
  527. // Note: While this could make assertions to get the correctly typed
  528. // values below, that would throw immediately while type system
  529. // validation with validateSchema() will produce more actionable
  530. // results.
  531. interfaces.push((getNamedType(type): any));
  532. }
  533. }
  534. return interfaces;
  535. }
  536. function buildUnionTypes(
  537. nodes: $ReadOnlyArray<UnionTypeDefinitionNode | UnionTypeExtensionNode>,
  538. ): Array<GraphQLObjectType> {
  539. const types = [];
  540. for (const node of nodes) {
  541. // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  542. const typeNodes = node.types ?? [];
  543. for (const type of typeNodes) {
  544. // Note: While this could make assertions to get the correctly typed
  545. // values below, that would throw immediately while type system
  546. // validation with validateSchema() will produce more actionable
  547. // results.
  548. types.push((getNamedType(type): any));
  549. }
  550. }
  551. return types;
  552. }
  553. function buildType(astNode: TypeDefinitionNode): GraphQLNamedType {
  554. const name = astNode.name.value;
  555. const description = getDescription(astNode, options);
  556. const extensionNodes = typeExtensionsMap[name] ?? [];
  557. switch (astNode.kind) {
  558. case Kind.OBJECT_TYPE_DEFINITION: {
  559. const extensionASTNodes = (extensionNodes: any);
  560. const allNodes = [astNode, ...extensionASTNodes];
  561. return new GraphQLObjectType({
  562. name,
  563. description,
  564. interfaces: () => buildInterfaces(allNodes),
  565. fields: () => buildFieldMap(allNodes),
  566. astNode,
  567. extensionASTNodes,
  568. });
  569. }
  570. case Kind.INTERFACE_TYPE_DEFINITION: {
  571. const extensionASTNodes = (extensionNodes: any);
  572. const allNodes = [astNode, ...extensionASTNodes];
  573. return new GraphQLInterfaceType({
  574. name,
  575. description,
  576. interfaces: () => buildInterfaces(allNodes),
  577. fields: () => buildFieldMap(allNodes),
  578. astNode,
  579. extensionASTNodes,
  580. });
  581. }
  582. case Kind.ENUM_TYPE_DEFINITION: {
  583. const extensionASTNodes = (extensionNodes: any);
  584. const allNodes = [astNode, ...extensionASTNodes];
  585. return new GraphQLEnumType({
  586. name,
  587. description,
  588. values: buildEnumValueMap(allNodes),
  589. astNode,
  590. extensionASTNodes,
  591. });
  592. }
  593. case Kind.UNION_TYPE_DEFINITION: {
  594. const extensionASTNodes = (extensionNodes: any);
  595. const allNodes = [astNode, ...extensionASTNodes];
  596. return new GraphQLUnionType({
  597. name,
  598. description,
  599. types: () => buildUnionTypes(allNodes),
  600. astNode,
  601. extensionASTNodes,
  602. });
  603. }
  604. case Kind.SCALAR_TYPE_DEFINITION: {
  605. const extensionASTNodes = (extensionNodes: any);
  606. return new GraphQLScalarType({
  607. name,
  608. description,
  609. specifiedByUrl: getSpecifiedByUrl(astNode),
  610. astNode,
  611. extensionASTNodes,
  612. });
  613. }
  614. case Kind.INPUT_OBJECT_TYPE_DEFINITION: {
  615. const extensionASTNodes = (extensionNodes: any);
  616. const allNodes = [astNode, ...extensionASTNodes];
  617. return new GraphQLInputObjectType({
  618. name,
  619. description,
  620. fields: () => buildInputFieldMap(allNodes),
  621. astNode,
  622. extensionASTNodes,
  623. });
  624. }
  625. }
  626. // istanbul ignore next (Not reachable. All possible type definition nodes have been considered)
  627. invariant(
  628. false,
  629. 'Unexpected type definition node: ' + inspect((astNode: empty)),
  630. );
  631. }
  632. }
  633. const stdTypeMap = keyMap(
  634. specifiedScalarTypes.concat(introspectionTypes),
  635. (type) => type.name,
  636. );
  637. /**
  638. * Given a field or enum value node, returns the string value for the
  639. * deprecation reason.
  640. */
  641. function getDeprecationReason(
  642. node:
  643. | EnumValueDefinitionNode
  644. | FieldDefinitionNode
  645. | InputValueDefinitionNode,
  646. ): ?string {
  647. const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
  648. return (deprecated?.reason: any);
  649. }
  650. /**
  651. * Given a scalar node, returns the string value for the specifiedByUrl.
  652. */
  653. function getSpecifiedByUrl(
  654. node: ScalarTypeDefinitionNode | ScalarTypeExtensionNode,
  655. ): ?string {
  656. const specifiedBy = getDirectiveValues(GraphQLSpecifiedByDirective, node);
  657. return (specifiedBy?.url: any);
  658. }
  659. /**
  660. * Given an ast node, returns its string description.
  661. * @deprecated: provided to ease adoption and will be removed in v16.
  662. *
  663. * Accepts options as a second argument:
  664. *
  665. * - commentDescriptions:
  666. * Provide true to use preceding comments as the description.
  667. *
  668. */
  669. export function getDescription(
  670. node: { +description?: StringValueNode, +loc?: Location, ... },
  671. options: ?{ commentDescriptions?: boolean, ... },
  672. ): void | string {
  673. if (node.description) {
  674. return node.description.value;
  675. }
  676. if (options?.commentDescriptions === true) {
  677. const rawValue = getLeadingCommentBlock(node);
  678. if (rawValue !== undefined) {
  679. return dedentBlockStringValue('\n' + rawValue);
  680. }
  681. }
  682. }
  683. function getLeadingCommentBlock(node): void | string {
  684. const loc = node.loc;
  685. if (!loc) {
  686. return;
  687. }
  688. const comments = [];
  689. let token = loc.startToken.prev;
  690. while (
  691. token != null &&
  692. token.kind === TokenKind.COMMENT &&
  693. token.next &&
  694. token.prev &&
  695. token.line + 1 === token.next.line &&
  696. token.line !== token.prev.line
  697. ) {
  698. const value = String(token.value);
  699. comments.push(value);
  700. token = token.prev;
  701. }
  702. return comments.length > 0 ? comments.reverse().join('\n') : undefined;
  703. }