parser.mjs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524
  1. import { syntaxError } from '../error/syntaxError.mjs';
  2. import { Location, OperationTypeNode } from './ast.mjs';
  3. import { DirectiveLocation } from './directiveLocation.mjs';
  4. import { Kind } from './kinds.mjs';
  5. import { isPunctuatorTokenKind, Lexer } from './lexer.mjs';
  6. import { isSource, Source } from './source.mjs';
  7. import { TokenKind } from './tokenKind.mjs';
  8. /**
  9. * Configuration options to control parser behavior
  10. */
  11. /**
  12. * Given a GraphQL source, parses it into a Document.
  13. * Throws GraphQLError if a syntax error is encountered.
  14. */
  15. export function parse(source, options) {
  16. const parser = new Parser(source, options);
  17. return parser.parseDocument();
  18. }
  19. /**
  20. * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
  21. * that value.
  22. * Throws GraphQLError if a syntax error is encountered.
  23. *
  24. * This is useful within tools that operate upon GraphQL Values directly and
  25. * in isolation of complete GraphQL documents.
  26. *
  27. * Consider providing the results to the utility function: valueFromAST().
  28. */
  29. export function parseValue(source, options) {
  30. const parser = new Parser(source, options);
  31. parser.expectToken(TokenKind.SOF);
  32. const value = parser.parseValueLiteral(false);
  33. parser.expectToken(TokenKind.EOF);
  34. return value;
  35. }
  36. /**
  37. * Similar to parseValue(), but raises a parse error if it encounters a
  38. * variable. The return type will be a constant value.
  39. */
  40. export function parseConstValue(source, options) {
  41. const parser = new Parser(source, options);
  42. parser.expectToken(TokenKind.SOF);
  43. const value = parser.parseConstValueLiteral();
  44. parser.expectToken(TokenKind.EOF);
  45. return value;
  46. }
  47. /**
  48. * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
  49. * that type.
  50. * Throws GraphQLError if a syntax error is encountered.
  51. *
  52. * This is useful within tools that operate upon GraphQL Types directly and
  53. * in isolation of complete GraphQL documents.
  54. *
  55. * Consider providing the results to the utility function: typeFromAST().
  56. */
  57. export function parseType(source, options) {
  58. const parser = new Parser(source, options);
  59. parser.expectToken(TokenKind.SOF);
  60. const type = parser.parseTypeReference();
  61. parser.expectToken(TokenKind.EOF);
  62. return type;
  63. }
  64. /**
  65. * This class is exported only to assist people in implementing their own parsers
  66. * without duplicating too much code and should be used only as last resort for cases
  67. * such as experimental syntax or if certain features could not be contributed upstream.
  68. *
  69. * It is still part of the internal API and is versioned, so any changes to it are never
  70. * considered breaking changes. If you still need to support multiple versions of the
  71. * library, please use the `versionInfo` variable for version detection.
  72. *
  73. * @internal
  74. */
  75. export class Parser {
  76. constructor(source, options = {}) {
  77. const sourceObj = isSource(source) ? source : new Source(source);
  78. this._lexer = new Lexer(sourceObj);
  79. this._options = options;
  80. this._tokenCounter = 0;
  81. }
  82. /**
  83. * Converts a name lex token into a name parse node.
  84. */
  85. parseName() {
  86. const token = this.expectToken(TokenKind.NAME);
  87. return this.node(token, {
  88. kind: Kind.NAME,
  89. value: token.value,
  90. });
  91. } // Implements the parsing rules in the Document section.
  92. /**
  93. * Document : Definition+
  94. */
  95. parseDocument() {
  96. return this.node(this._lexer.token, {
  97. kind: Kind.DOCUMENT,
  98. definitions: this.many(
  99. TokenKind.SOF,
  100. this.parseDefinition,
  101. TokenKind.EOF,
  102. ),
  103. });
  104. }
  105. /**
  106. * Definition :
  107. * - ExecutableDefinition
  108. * - TypeSystemDefinition
  109. * - TypeSystemExtension
  110. *
  111. * ExecutableDefinition :
  112. * - OperationDefinition
  113. * - FragmentDefinition
  114. *
  115. * TypeSystemDefinition :
  116. * - SchemaDefinition
  117. * - TypeDefinition
  118. * - DirectiveDefinition
  119. *
  120. * TypeDefinition :
  121. * - ScalarTypeDefinition
  122. * - ObjectTypeDefinition
  123. * - InterfaceTypeDefinition
  124. * - UnionTypeDefinition
  125. * - EnumTypeDefinition
  126. * - InputObjectTypeDefinition
  127. */
  128. parseDefinition() {
  129. if (this.peek(TokenKind.BRACE_L)) {
  130. return this.parseOperationDefinition();
  131. } // Many definitions begin with a description and require a lookahead.
  132. const hasDescription = this.peekDescription();
  133. const keywordToken = hasDescription
  134. ? this._lexer.lookahead()
  135. : this._lexer.token;
  136. if (keywordToken.kind === TokenKind.NAME) {
  137. switch (keywordToken.value) {
  138. case 'schema':
  139. return this.parseSchemaDefinition();
  140. case 'scalar':
  141. return this.parseScalarTypeDefinition();
  142. case 'type':
  143. return this.parseObjectTypeDefinition();
  144. case 'interface':
  145. return this.parseInterfaceTypeDefinition();
  146. case 'union':
  147. return this.parseUnionTypeDefinition();
  148. case 'enum':
  149. return this.parseEnumTypeDefinition();
  150. case 'input':
  151. return this.parseInputObjectTypeDefinition();
  152. case 'directive':
  153. return this.parseDirectiveDefinition();
  154. }
  155. if (hasDescription) {
  156. throw syntaxError(
  157. this._lexer.source,
  158. this._lexer.token.start,
  159. 'Unexpected description, descriptions are supported only on type definitions.',
  160. );
  161. }
  162. switch (keywordToken.value) {
  163. case 'query':
  164. case 'mutation':
  165. case 'subscription':
  166. return this.parseOperationDefinition();
  167. case 'fragment':
  168. return this.parseFragmentDefinition();
  169. case 'extend':
  170. return this.parseTypeSystemExtension();
  171. }
  172. }
  173. throw this.unexpected(keywordToken);
  174. } // Implements the parsing rules in the Operations section.
  175. /**
  176. * OperationDefinition :
  177. * - SelectionSet
  178. * - OperationType Name? VariableDefinitions? Directives? SelectionSet
  179. */
  180. parseOperationDefinition() {
  181. const start = this._lexer.token;
  182. if (this.peek(TokenKind.BRACE_L)) {
  183. return this.node(start, {
  184. kind: Kind.OPERATION_DEFINITION,
  185. operation: OperationTypeNode.QUERY,
  186. name: undefined,
  187. variableDefinitions: [],
  188. directives: [],
  189. selectionSet: this.parseSelectionSet(),
  190. });
  191. }
  192. const operation = this.parseOperationType();
  193. let name;
  194. if (this.peek(TokenKind.NAME)) {
  195. name = this.parseName();
  196. }
  197. return this.node(start, {
  198. kind: Kind.OPERATION_DEFINITION,
  199. operation,
  200. name,
  201. variableDefinitions: this.parseVariableDefinitions(),
  202. directives: this.parseDirectives(false),
  203. selectionSet: this.parseSelectionSet(),
  204. });
  205. }
  206. /**
  207. * OperationType : one of query mutation subscription
  208. */
  209. parseOperationType() {
  210. const operationToken = this.expectToken(TokenKind.NAME);
  211. switch (operationToken.value) {
  212. case 'query':
  213. return OperationTypeNode.QUERY;
  214. case 'mutation':
  215. return OperationTypeNode.MUTATION;
  216. case 'subscription':
  217. return OperationTypeNode.SUBSCRIPTION;
  218. }
  219. throw this.unexpected(operationToken);
  220. }
  221. /**
  222. * VariableDefinitions : ( VariableDefinition+ )
  223. */
  224. parseVariableDefinitions() {
  225. return this.optionalMany(
  226. TokenKind.PAREN_L,
  227. this.parseVariableDefinition,
  228. TokenKind.PAREN_R,
  229. );
  230. }
  231. /**
  232. * VariableDefinition : Variable : Type DefaultValue? Directives[Const]?
  233. */
  234. parseVariableDefinition() {
  235. return this.node(this._lexer.token, {
  236. kind: Kind.VARIABLE_DEFINITION,
  237. variable: this.parseVariable(),
  238. type: (this.expectToken(TokenKind.COLON), this.parseTypeReference()),
  239. defaultValue: this.expectOptionalToken(TokenKind.EQUALS)
  240. ? this.parseConstValueLiteral()
  241. : undefined,
  242. directives: this.parseConstDirectives(),
  243. });
  244. }
  245. /**
  246. * Variable : $ Name
  247. */
  248. parseVariable() {
  249. const start = this._lexer.token;
  250. this.expectToken(TokenKind.DOLLAR);
  251. return this.node(start, {
  252. kind: Kind.VARIABLE,
  253. name: this.parseName(),
  254. });
  255. }
  256. /**
  257. * ```
  258. * SelectionSet : { Selection+ }
  259. * ```
  260. */
  261. parseSelectionSet() {
  262. return this.node(this._lexer.token, {
  263. kind: Kind.SELECTION_SET,
  264. selections: this.many(
  265. TokenKind.BRACE_L,
  266. this.parseSelection,
  267. TokenKind.BRACE_R,
  268. ),
  269. });
  270. }
  271. /**
  272. * Selection :
  273. * - Field
  274. * - FragmentSpread
  275. * - InlineFragment
  276. */
  277. parseSelection() {
  278. return this.peek(TokenKind.SPREAD)
  279. ? this.parseFragment()
  280. : this.parseField();
  281. }
  282. /**
  283. * Field : Alias? Name Arguments? Directives? SelectionSet?
  284. *
  285. * Alias : Name :
  286. */
  287. parseField() {
  288. const start = this._lexer.token;
  289. const nameOrAlias = this.parseName();
  290. let alias;
  291. let name;
  292. if (this.expectOptionalToken(TokenKind.COLON)) {
  293. alias = nameOrAlias;
  294. name = this.parseName();
  295. } else {
  296. name = nameOrAlias;
  297. }
  298. return this.node(start, {
  299. kind: Kind.FIELD,
  300. alias,
  301. name,
  302. arguments: this.parseArguments(false),
  303. directives: this.parseDirectives(false),
  304. selectionSet: this.peek(TokenKind.BRACE_L)
  305. ? this.parseSelectionSet()
  306. : undefined,
  307. });
  308. }
  309. /**
  310. * Arguments[Const] : ( Argument[?Const]+ )
  311. */
  312. parseArguments(isConst) {
  313. const item = isConst ? this.parseConstArgument : this.parseArgument;
  314. return this.optionalMany(TokenKind.PAREN_L, item, TokenKind.PAREN_R);
  315. }
  316. /**
  317. * Argument[Const] : Name : Value[?Const]
  318. */
  319. parseArgument(isConst = false) {
  320. const start = this._lexer.token;
  321. const name = this.parseName();
  322. this.expectToken(TokenKind.COLON);
  323. return this.node(start, {
  324. kind: Kind.ARGUMENT,
  325. name,
  326. value: this.parseValueLiteral(isConst),
  327. });
  328. }
  329. parseConstArgument() {
  330. return this.parseArgument(true);
  331. } // Implements the parsing rules in the Fragments section.
  332. /**
  333. * Corresponds to both FragmentSpread and InlineFragment in the spec.
  334. *
  335. * FragmentSpread : ... FragmentName Directives?
  336. *
  337. * InlineFragment : ... TypeCondition? Directives? SelectionSet
  338. */
  339. parseFragment() {
  340. const start = this._lexer.token;
  341. this.expectToken(TokenKind.SPREAD);
  342. const hasTypeCondition = this.expectOptionalKeyword('on');
  343. if (!hasTypeCondition && this.peek(TokenKind.NAME)) {
  344. return this.node(start, {
  345. kind: Kind.FRAGMENT_SPREAD,
  346. name: this.parseFragmentName(),
  347. directives: this.parseDirectives(false),
  348. });
  349. }
  350. return this.node(start, {
  351. kind: Kind.INLINE_FRAGMENT,
  352. typeCondition: hasTypeCondition ? this.parseNamedType() : undefined,
  353. directives: this.parseDirectives(false),
  354. selectionSet: this.parseSelectionSet(),
  355. });
  356. }
  357. /**
  358. * FragmentDefinition :
  359. * - fragment FragmentName on TypeCondition Directives? SelectionSet
  360. *
  361. * TypeCondition : NamedType
  362. */
  363. parseFragmentDefinition() {
  364. const start = this._lexer.token;
  365. this.expectKeyword('fragment'); // Legacy support for defining variables within fragments changes
  366. // the grammar of FragmentDefinition:
  367. // - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet
  368. if (this._options.allowLegacyFragmentVariables === true) {
  369. return this.node(start, {
  370. kind: Kind.FRAGMENT_DEFINITION,
  371. name: this.parseFragmentName(),
  372. variableDefinitions: this.parseVariableDefinitions(),
  373. typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
  374. directives: this.parseDirectives(false),
  375. selectionSet: this.parseSelectionSet(),
  376. });
  377. }
  378. return this.node(start, {
  379. kind: Kind.FRAGMENT_DEFINITION,
  380. name: this.parseFragmentName(),
  381. typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
  382. directives: this.parseDirectives(false),
  383. selectionSet: this.parseSelectionSet(),
  384. });
  385. }
  386. /**
  387. * FragmentName : Name but not `on`
  388. */
  389. parseFragmentName() {
  390. if (this._lexer.token.value === 'on') {
  391. throw this.unexpected();
  392. }
  393. return this.parseName();
  394. } // Implements the parsing rules in the Values section.
  395. /**
  396. * Value[Const] :
  397. * - [~Const] Variable
  398. * - IntValue
  399. * - FloatValue
  400. * - StringValue
  401. * - BooleanValue
  402. * - NullValue
  403. * - EnumValue
  404. * - ListValue[?Const]
  405. * - ObjectValue[?Const]
  406. *
  407. * BooleanValue : one of `true` `false`
  408. *
  409. * NullValue : `null`
  410. *
  411. * EnumValue : Name but not `true`, `false` or `null`
  412. */
  413. parseValueLiteral(isConst) {
  414. const token = this._lexer.token;
  415. switch (token.kind) {
  416. case TokenKind.BRACKET_L:
  417. return this.parseList(isConst);
  418. case TokenKind.BRACE_L:
  419. return this.parseObject(isConst);
  420. case TokenKind.INT:
  421. this.advanceLexer();
  422. return this.node(token, {
  423. kind: Kind.INT,
  424. value: token.value,
  425. });
  426. case TokenKind.FLOAT:
  427. this.advanceLexer();
  428. return this.node(token, {
  429. kind: Kind.FLOAT,
  430. value: token.value,
  431. });
  432. case TokenKind.STRING:
  433. case TokenKind.BLOCK_STRING:
  434. return this.parseStringLiteral();
  435. case TokenKind.NAME:
  436. this.advanceLexer();
  437. switch (token.value) {
  438. case 'true':
  439. return this.node(token, {
  440. kind: Kind.BOOLEAN,
  441. value: true,
  442. });
  443. case 'false':
  444. return this.node(token, {
  445. kind: Kind.BOOLEAN,
  446. value: false,
  447. });
  448. case 'null':
  449. return this.node(token, {
  450. kind: Kind.NULL,
  451. });
  452. default:
  453. return this.node(token, {
  454. kind: Kind.ENUM,
  455. value: token.value,
  456. });
  457. }
  458. case TokenKind.DOLLAR:
  459. if (isConst) {
  460. this.expectToken(TokenKind.DOLLAR);
  461. if (this._lexer.token.kind === TokenKind.NAME) {
  462. const varName = this._lexer.token.value;
  463. throw syntaxError(
  464. this._lexer.source,
  465. token.start,
  466. `Unexpected variable "$${varName}" in constant value.`,
  467. );
  468. } else {
  469. throw this.unexpected(token);
  470. }
  471. }
  472. return this.parseVariable();
  473. default:
  474. throw this.unexpected();
  475. }
  476. }
  477. parseConstValueLiteral() {
  478. return this.parseValueLiteral(true);
  479. }
  480. parseStringLiteral() {
  481. const token = this._lexer.token;
  482. this.advanceLexer();
  483. return this.node(token, {
  484. kind: Kind.STRING,
  485. value: token.value,
  486. block: token.kind === TokenKind.BLOCK_STRING,
  487. });
  488. }
  489. /**
  490. * ListValue[Const] :
  491. * - [ ]
  492. * - [ Value[?Const]+ ]
  493. */
  494. parseList(isConst) {
  495. const item = () => this.parseValueLiteral(isConst);
  496. return this.node(this._lexer.token, {
  497. kind: Kind.LIST,
  498. values: this.any(TokenKind.BRACKET_L, item, TokenKind.BRACKET_R),
  499. });
  500. }
  501. /**
  502. * ```
  503. * ObjectValue[Const] :
  504. * - { }
  505. * - { ObjectField[?Const]+ }
  506. * ```
  507. */
  508. parseObject(isConst) {
  509. const item = () => this.parseObjectField(isConst);
  510. return this.node(this._lexer.token, {
  511. kind: Kind.OBJECT,
  512. fields: this.any(TokenKind.BRACE_L, item, TokenKind.BRACE_R),
  513. });
  514. }
  515. /**
  516. * ObjectField[Const] : Name : Value[?Const]
  517. */
  518. parseObjectField(isConst) {
  519. const start = this._lexer.token;
  520. const name = this.parseName();
  521. this.expectToken(TokenKind.COLON);
  522. return this.node(start, {
  523. kind: Kind.OBJECT_FIELD,
  524. name,
  525. value: this.parseValueLiteral(isConst),
  526. });
  527. } // Implements the parsing rules in the Directives section.
  528. /**
  529. * Directives[Const] : Directive[?Const]+
  530. */
  531. parseDirectives(isConst) {
  532. const directives = [];
  533. while (this.peek(TokenKind.AT)) {
  534. directives.push(this.parseDirective(isConst));
  535. }
  536. return directives;
  537. }
  538. parseConstDirectives() {
  539. return this.parseDirectives(true);
  540. }
  541. /**
  542. * ```
  543. * Directive[Const] : @ Name Arguments[?Const]?
  544. * ```
  545. */
  546. parseDirective(isConst) {
  547. const start = this._lexer.token;
  548. this.expectToken(TokenKind.AT);
  549. return this.node(start, {
  550. kind: Kind.DIRECTIVE,
  551. name: this.parseName(),
  552. arguments: this.parseArguments(isConst),
  553. });
  554. } // Implements the parsing rules in the Types section.
  555. /**
  556. * Type :
  557. * - NamedType
  558. * - ListType
  559. * - NonNullType
  560. */
  561. parseTypeReference() {
  562. const start = this._lexer.token;
  563. let type;
  564. if (this.expectOptionalToken(TokenKind.BRACKET_L)) {
  565. const innerType = this.parseTypeReference();
  566. this.expectToken(TokenKind.BRACKET_R);
  567. type = this.node(start, {
  568. kind: Kind.LIST_TYPE,
  569. type: innerType,
  570. });
  571. } else {
  572. type = this.parseNamedType();
  573. }
  574. if (this.expectOptionalToken(TokenKind.BANG)) {
  575. return this.node(start, {
  576. kind: Kind.NON_NULL_TYPE,
  577. type,
  578. });
  579. }
  580. return type;
  581. }
  582. /**
  583. * NamedType : Name
  584. */
  585. parseNamedType() {
  586. return this.node(this._lexer.token, {
  587. kind: Kind.NAMED_TYPE,
  588. name: this.parseName(),
  589. });
  590. } // Implements the parsing rules in the Type Definition section.
  591. peekDescription() {
  592. return this.peek(TokenKind.STRING) || this.peek(TokenKind.BLOCK_STRING);
  593. }
  594. /**
  595. * Description : StringValue
  596. */
  597. parseDescription() {
  598. if (this.peekDescription()) {
  599. return this.parseStringLiteral();
  600. }
  601. }
  602. /**
  603. * ```
  604. * SchemaDefinition : Description? schema Directives[Const]? { OperationTypeDefinition+ }
  605. * ```
  606. */
  607. parseSchemaDefinition() {
  608. const start = this._lexer.token;
  609. const description = this.parseDescription();
  610. this.expectKeyword('schema');
  611. const directives = this.parseConstDirectives();
  612. const operationTypes = this.many(
  613. TokenKind.BRACE_L,
  614. this.parseOperationTypeDefinition,
  615. TokenKind.BRACE_R,
  616. );
  617. return this.node(start, {
  618. kind: Kind.SCHEMA_DEFINITION,
  619. description,
  620. directives,
  621. operationTypes,
  622. });
  623. }
  624. /**
  625. * OperationTypeDefinition : OperationType : NamedType
  626. */
  627. parseOperationTypeDefinition() {
  628. const start = this._lexer.token;
  629. const operation = this.parseOperationType();
  630. this.expectToken(TokenKind.COLON);
  631. const type = this.parseNamedType();
  632. return this.node(start, {
  633. kind: Kind.OPERATION_TYPE_DEFINITION,
  634. operation,
  635. type,
  636. });
  637. }
  638. /**
  639. * ScalarTypeDefinition : Description? scalar Name Directives[Const]?
  640. */
  641. parseScalarTypeDefinition() {
  642. const start = this._lexer.token;
  643. const description = this.parseDescription();
  644. this.expectKeyword('scalar');
  645. const name = this.parseName();
  646. const directives = this.parseConstDirectives();
  647. return this.node(start, {
  648. kind: Kind.SCALAR_TYPE_DEFINITION,
  649. description,
  650. name,
  651. directives,
  652. });
  653. }
  654. /**
  655. * ObjectTypeDefinition :
  656. * Description?
  657. * type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
  658. */
  659. parseObjectTypeDefinition() {
  660. const start = this._lexer.token;
  661. const description = this.parseDescription();
  662. this.expectKeyword('type');
  663. const name = this.parseName();
  664. const interfaces = this.parseImplementsInterfaces();
  665. const directives = this.parseConstDirectives();
  666. const fields = this.parseFieldsDefinition();
  667. return this.node(start, {
  668. kind: Kind.OBJECT_TYPE_DEFINITION,
  669. description,
  670. name,
  671. interfaces,
  672. directives,
  673. fields,
  674. });
  675. }
  676. /**
  677. * ImplementsInterfaces :
  678. * - implements `&`? NamedType
  679. * - ImplementsInterfaces & NamedType
  680. */
  681. parseImplementsInterfaces() {
  682. return this.expectOptionalKeyword('implements')
  683. ? this.delimitedMany(TokenKind.AMP, this.parseNamedType)
  684. : [];
  685. }
  686. /**
  687. * ```
  688. * FieldsDefinition : { FieldDefinition+ }
  689. * ```
  690. */
  691. parseFieldsDefinition() {
  692. return this.optionalMany(
  693. TokenKind.BRACE_L,
  694. this.parseFieldDefinition,
  695. TokenKind.BRACE_R,
  696. );
  697. }
  698. /**
  699. * FieldDefinition :
  700. * - Description? Name ArgumentsDefinition? : Type Directives[Const]?
  701. */
  702. parseFieldDefinition() {
  703. const start = this._lexer.token;
  704. const description = this.parseDescription();
  705. const name = this.parseName();
  706. const args = this.parseArgumentDefs();
  707. this.expectToken(TokenKind.COLON);
  708. const type = this.parseTypeReference();
  709. const directives = this.parseConstDirectives();
  710. return this.node(start, {
  711. kind: Kind.FIELD_DEFINITION,
  712. description,
  713. name,
  714. arguments: args,
  715. type,
  716. directives,
  717. });
  718. }
  719. /**
  720. * ArgumentsDefinition : ( InputValueDefinition+ )
  721. */
  722. parseArgumentDefs() {
  723. return this.optionalMany(
  724. TokenKind.PAREN_L,
  725. this.parseInputValueDef,
  726. TokenKind.PAREN_R,
  727. );
  728. }
  729. /**
  730. * InputValueDefinition :
  731. * - Description? Name : Type DefaultValue? Directives[Const]?
  732. */
  733. parseInputValueDef() {
  734. const start = this._lexer.token;
  735. const description = this.parseDescription();
  736. const name = this.parseName();
  737. this.expectToken(TokenKind.COLON);
  738. const type = this.parseTypeReference();
  739. let defaultValue;
  740. if (this.expectOptionalToken(TokenKind.EQUALS)) {
  741. defaultValue = this.parseConstValueLiteral();
  742. }
  743. const directives = this.parseConstDirectives();
  744. return this.node(start, {
  745. kind: Kind.INPUT_VALUE_DEFINITION,
  746. description,
  747. name,
  748. type,
  749. defaultValue,
  750. directives,
  751. });
  752. }
  753. /**
  754. * InterfaceTypeDefinition :
  755. * - Description? interface Name Directives[Const]? FieldsDefinition?
  756. */
  757. parseInterfaceTypeDefinition() {
  758. const start = this._lexer.token;
  759. const description = this.parseDescription();
  760. this.expectKeyword('interface');
  761. const name = this.parseName();
  762. const interfaces = this.parseImplementsInterfaces();
  763. const directives = this.parseConstDirectives();
  764. const fields = this.parseFieldsDefinition();
  765. return this.node(start, {
  766. kind: Kind.INTERFACE_TYPE_DEFINITION,
  767. description,
  768. name,
  769. interfaces,
  770. directives,
  771. fields,
  772. });
  773. }
  774. /**
  775. * UnionTypeDefinition :
  776. * - Description? union Name Directives[Const]? UnionMemberTypes?
  777. */
  778. parseUnionTypeDefinition() {
  779. const start = this._lexer.token;
  780. const description = this.parseDescription();
  781. this.expectKeyword('union');
  782. const name = this.parseName();
  783. const directives = this.parseConstDirectives();
  784. const types = this.parseUnionMemberTypes();
  785. return this.node(start, {
  786. kind: Kind.UNION_TYPE_DEFINITION,
  787. description,
  788. name,
  789. directives,
  790. types,
  791. });
  792. }
  793. /**
  794. * UnionMemberTypes :
  795. * - = `|`? NamedType
  796. * - UnionMemberTypes | NamedType
  797. */
  798. parseUnionMemberTypes() {
  799. return this.expectOptionalToken(TokenKind.EQUALS)
  800. ? this.delimitedMany(TokenKind.PIPE, this.parseNamedType)
  801. : [];
  802. }
  803. /**
  804. * EnumTypeDefinition :
  805. * - Description? enum Name Directives[Const]? EnumValuesDefinition?
  806. */
  807. parseEnumTypeDefinition() {
  808. const start = this._lexer.token;
  809. const description = this.parseDescription();
  810. this.expectKeyword('enum');
  811. const name = this.parseName();
  812. const directives = this.parseConstDirectives();
  813. const values = this.parseEnumValuesDefinition();
  814. return this.node(start, {
  815. kind: Kind.ENUM_TYPE_DEFINITION,
  816. description,
  817. name,
  818. directives,
  819. values,
  820. });
  821. }
  822. /**
  823. * ```
  824. * EnumValuesDefinition : { EnumValueDefinition+ }
  825. * ```
  826. */
  827. parseEnumValuesDefinition() {
  828. return this.optionalMany(
  829. TokenKind.BRACE_L,
  830. this.parseEnumValueDefinition,
  831. TokenKind.BRACE_R,
  832. );
  833. }
  834. /**
  835. * EnumValueDefinition : Description? EnumValue Directives[Const]?
  836. */
  837. parseEnumValueDefinition() {
  838. const start = this._lexer.token;
  839. const description = this.parseDescription();
  840. const name = this.parseEnumValueName();
  841. const directives = this.parseConstDirectives();
  842. return this.node(start, {
  843. kind: Kind.ENUM_VALUE_DEFINITION,
  844. description,
  845. name,
  846. directives,
  847. });
  848. }
  849. /**
  850. * EnumValue : Name but not `true`, `false` or `null`
  851. */
  852. parseEnumValueName() {
  853. if (
  854. this._lexer.token.value === 'true' ||
  855. this._lexer.token.value === 'false' ||
  856. this._lexer.token.value === 'null'
  857. ) {
  858. throw syntaxError(
  859. this._lexer.source,
  860. this._lexer.token.start,
  861. `${getTokenDesc(
  862. this._lexer.token,
  863. )} is reserved and cannot be used for an enum value.`,
  864. );
  865. }
  866. return this.parseName();
  867. }
  868. /**
  869. * InputObjectTypeDefinition :
  870. * - Description? input Name Directives[Const]? InputFieldsDefinition?
  871. */
  872. parseInputObjectTypeDefinition() {
  873. const start = this._lexer.token;
  874. const description = this.parseDescription();
  875. this.expectKeyword('input');
  876. const name = this.parseName();
  877. const directives = this.parseConstDirectives();
  878. const fields = this.parseInputFieldsDefinition();
  879. return this.node(start, {
  880. kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
  881. description,
  882. name,
  883. directives,
  884. fields,
  885. });
  886. }
  887. /**
  888. * ```
  889. * InputFieldsDefinition : { InputValueDefinition+ }
  890. * ```
  891. */
  892. parseInputFieldsDefinition() {
  893. return this.optionalMany(
  894. TokenKind.BRACE_L,
  895. this.parseInputValueDef,
  896. TokenKind.BRACE_R,
  897. );
  898. }
  899. /**
  900. * TypeSystemExtension :
  901. * - SchemaExtension
  902. * - TypeExtension
  903. *
  904. * TypeExtension :
  905. * - ScalarTypeExtension
  906. * - ObjectTypeExtension
  907. * - InterfaceTypeExtension
  908. * - UnionTypeExtension
  909. * - EnumTypeExtension
  910. * - InputObjectTypeDefinition
  911. */
  912. parseTypeSystemExtension() {
  913. const keywordToken = this._lexer.lookahead();
  914. if (keywordToken.kind === TokenKind.NAME) {
  915. switch (keywordToken.value) {
  916. case 'schema':
  917. return this.parseSchemaExtension();
  918. case 'scalar':
  919. return this.parseScalarTypeExtension();
  920. case 'type':
  921. return this.parseObjectTypeExtension();
  922. case 'interface':
  923. return this.parseInterfaceTypeExtension();
  924. case 'union':
  925. return this.parseUnionTypeExtension();
  926. case 'enum':
  927. return this.parseEnumTypeExtension();
  928. case 'input':
  929. return this.parseInputObjectTypeExtension();
  930. }
  931. }
  932. throw this.unexpected(keywordToken);
  933. }
  934. /**
  935. * ```
  936. * SchemaExtension :
  937. * - extend schema Directives[Const]? { OperationTypeDefinition+ }
  938. * - extend schema Directives[Const]
  939. * ```
  940. */
  941. parseSchemaExtension() {
  942. const start = this._lexer.token;
  943. this.expectKeyword('extend');
  944. this.expectKeyword('schema');
  945. const directives = this.parseConstDirectives();
  946. const operationTypes = this.optionalMany(
  947. TokenKind.BRACE_L,
  948. this.parseOperationTypeDefinition,
  949. TokenKind.BRACE_R,
  950. );
  951. if (directives.length === 0 && operationTypes.length === 0) {
  952. throw this.unexpected();
  953. }
  954. return this.node(start, {
  955. kind: Kind.SCHEMA_EXTENSION,
  956. directives,
  957. operationTypes,
  958. });
  959. }
  960. /**
  961. * ScalarTypeExtension :
  962. * - extend scalar Name Directives[Const]
  963. */
  964. parseScalarTypeExtension() {
  965. const start = this._lexer.token;
  966. this.expectKeyword('extend');
  967. this.expectKeyword('scalar');
  968. const name = this.parseName();
  969. const directives = this.parseConstDirectives();
  970. if (directives.length === 0) {
  971. throw this.unexpected();
  972. }
  973. return this.node(start, {
  974. kind: Kind.SCALAR_TYPE_EXTENSION,
  975. name,
  976. directives,
  977. });
  978. }
  979. /**
  980. * ObjectTypeExtension :
  981. * - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
  982. * - extend type Name ImplementsInterfaces? Directives[Const]
  983. * - extend type Name ImplementsInterfaces
  984. */
  985. parseObjectTypeExtension() {
  986. const start = this._lexer.token;
  987. this.expectKeyword('extend');
  988. this.expectKeyword('type');
  989. const name = this.parseName();
  990. const interfaces = this.parseImplementsInterfaces();
  991. const directives = this.parseConstDirectives();
  992. const fields = this.parseFieldsDefinition();
  993. if (
  994. interfaces.length === 0 &&
  995. directives.length === 0 &&
  996. fields.length === 0
  997. ) {
  998. throw this.unexpected();
  999. }
  1000. return this.node(start, {
  1001. kind: Kind.OBJECT_TYPE_EXTENSION,
  1002. name,
  1003. interfaces,
  1004. directives,
  1005. fields,
  1006. });
  1007. }
  1008. /**
  1009. * InterfaceTypeExtension :
  1010. * - extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
  1011. * - extend interface Name ImplementsInterfaces? Directives[Const]
  1012. * - extend interface Name ImplementsInterfaces
  1013. */
  1014. parseInterfaceTypeExtension() {
  1015. const start = this._lexer.token;
  1016. this.expectKeyword('extend');
  1017. this.expectKeyword('interface');
  1018. const name = this.parseName();
  1019. const interfaces = this.parseImplementsInterfaces();
  1020. const directives = this.parseConstDirectives();
  1021. const fields = this.parseFieldsDefinition();
  1022. if (
  1023. interfaces.length === 0 &&
  1024. directives.length === 0 &&
  1025. fields.length === 0
  1026. ) {
  1027. throw this.unexpected();
  1028. }
  1029. return this.node(start, {
  1030. kind: Kind.INTERFACE_TYPE_EXTENSION,
  1031. name,
  1032. interfaces,
  1033. directives,
  1034. fields,
  1035. });
  1036. }
  1037. /**
  1038. * UnionTypeExtension :
  1039. * - extend union Name Directives[Const]? UnionMemberTypes
  1040. * - extend union Name Directives[Const]
  1041. */
  1042. parseUnionTypeExtension() {
  1043. const start = this._lexer.token;
  1044. this.expectKeyword('extend');
  1045. this.expectKeyword('union');
  1046. const name = this.parseName();
  1047. const directives = this.parseConstDirectives();
  1048. const types = this.parseUnionMemberTypes();
  1049. if (directives.length === 0 && types.length === 0) {
  1050. throw this.unexpected();
  1051. }
  1052. return this.node(start, {
  1053. kind: Kind.UNION_TYPE_EXTENSION,
  1054. name,
  1055. directives,
  1056. types,
  1057. });
  1058. }
  1059. /**
  1060. * EnumTypeExtension :
  1061. * - extend enum Name Directives[Const]? EnumValuesDefinition
  1062. * - extend enum Name Directives[Const]
  1063. */
  1064. parseEnumTypeExtension() {
  1065. const start = this._lexer.token;
  1066. this.expectKeyword('extend');
  1067. this.expectKeyword('enum');
  1068. const name = this.parseName();
  1069. const directives = this.parseConstDirectives();
  1070. const values = this.parseEnumValuesDefinition();
  1071. if (directives.length === 0 && values.length === 0) {
  1072. throw this.unexpected();
  1073. }
  1074. return this.node(start, {
  1075. kind: Kind.ENUM_TYPE_EXTENSION,
  1076. name,
  1077. directives,
  1078. values,
  1079. });
  1080. }
  1081. /**
  1082. * InputObjectTypeExtension :
  1083. * - extend input Name Directives[Const]? InputFieldsDefinition
  1084. * - extend input Name Directives[Const]
  1085. */
  1086. parseInputObjectTypeExtension() {
  1087. const start = this._lexer.token;
  1088. this.expectKeyword('extend');
  1089. this.expectKeyword('input');
  1090. const name = this.parseName();
  1091. const directives = this.parseConstDirectives();
  1092. const fields = this.parseInputFieldsDefinition();
  1093. if (directives.length === 0 && fields.length === 0) {
  1094. throw this.unexpected();
  1095. }
  1096. return this.node(start, {
  1097. kind: Kind.INPUT_OBJECT_TYPE_EXTENSION,
  1098. name,
  1099. directives,
  1100. fields,
  1101. });
  1102. }
  1103. /**
  1104. * ```
  1105. * DirectiveDefinition :
  1106. * - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations
  1107. * ```
  1108. */
  1109. parseDirectiveDefinition() {
  1110. const start = this._lexer.token;
  1111. const description = this.parseDescription();
  1112. this.expectKeyword('directive');
  1113. this.expectToken(TokenKind.AT);
  1114. const name = this.parseName();
  1115. const args = this.parseArgumentDefs();
  1116. const repeatable = this.expectOptionalKeyword('repeatable');
  1117. this.expectKeyword('on');
  1118. const locations = this.parseDirectiveLocations();
  1119. return this.node(start, {
  1120. kind: Kind.DIRECTIVE_DEFINITION,
  1121. description,
  1122. name,
  1123. arguments: args,
  1124. repeatable,
  1125. locations,
  1126. });
  1127. }
  1128. /**
  1129. * DirectiveLocations :
  1130. * - `|`? DirectiveLocation
  1131. * - DirectiveLocations | DirectiveLocation
  1132. */
  1133. parseDirectiveLocations() {
  1134. return this.delimitedMany(TokenKind.PIPE, this.parseDirectiveLocation);
  1135. }
  1136. /*
  1137. * DirectiveLocation :
  1138. * - ExecutableDirectiveLocation
  1139. * - TypeSystemDirectiveLocation
  1140. *
  1141. * ExecutableDirectiveLocation : one of
  1142. * `QUERY`
  1143. * `MUTATION`
  1144. * `SUBSCRIPTION`
  1145. * `FIELD`
  1146. * `FRAGMENT_DEFINITION`
  1147. * `FRAGMENT_SPREAD`
  1148. * `INLINE_FRAGMENT`
  1149. *
  1150. * TypeSystemDirectiveLocation : one of
  1151. * `SCHEMA`
  1152. * `SCALAR`
  1153. * `OBJECT`
  1154. * `FIELD_DEFINITION`
  1155. * `ARGUMENT_DEFINITION`
  1156. * `INTERFACE`
  1157. * `UNION`
  1158. * `ENUM`
  1159. * `ENUM_VALUE`
  1160. * `INPUT_OBJECT`
  1161. * `INPUT_FIELD_DEFINITION`
  1162. */
  1163. parseDirectiveLocation() {
  1164. const start = this._lexer.token;
  1165. const name = this.parseName();
  1166. if (Object.prototype.hasOwnProperty.call(DirectiveLocation, name.value)) {
  1167. return name;
  1168. }
  1169. throw this.unexpected(start);
  1170. } // Core parsing utility functions
  1171. /**
  1172. * Returns a node that, if configured to do so, sets a "loc" field as a
  1173. * location object, used to identify the place in the source that created a
  1174. * given parsed object.
  1175. */
  1176. node(startToken, node) {
  1177. if (this._options.noLocation !== true) {
  1178. node.loc = new Location(
  1179. startToken,
  1180. this._lexer.lastToken,
  1181. this._lexer.source,
  1182. );
  1183. }
  1184. return node;
  1185. }
  1186. /**
  1187. * Determines if the next token is of a given kind
  1188. */
  1189. peek(kind) {
  1190. return this._lexer.token.kind === kind;
  1191. }
  1192. /**
  1193. * If the next token is of the given kind, return that token after advancing the lexer.
  1194. * Otherwise, do not change the parser state and throw an error.
  1195. */
  1196. expectToken(kind) {
  1197. const token = this._lexer.token;
  1198. if (token.kind === kind) {
  1199. this.advanceLexer();
  1200. return token;
  1201. }
  1202. throw syntaxError(
  1203. this._lexer.source,
  1204. token.start,
  1205. `Expected ${getTokenKindDesc(kind)}, found ${getTokenDesc(token)}.`,
  1206. );
  1207. }
  1208. /**
  1209. * If the next token is of the given kind, return "true" after advancing the lexer.
  1210. * Otherwise, do not change the parser state and return "false".
  1211. */
  1212. expectOptionalToken(kind) {
  1213. const token = this._lexer.token;
  1214. if (token.kind === kind) {
  1215. this.advanceLexer();
  1216. return true;
  1217. }
  1218. return false;
  1219. }
  1220. /**
  1221. * If the next token is a given keyword, advance the lexer.
  1222. * Otherwise, do not change the parser state and throw an error.
  1223. */
  1224. expectKeyword(value) {
  1225. const token = this._lexer.token;
  1226. if (token.kind === TokenKind.NAME && token.value === value) {
  1227. this.advanceLexer();
  1228. } else {
  1229. throw syntaxError(
  1230. this._lexer.source,
  1231. token.start,
  1232. `Expected "${value}", found ${getTokenDesc(token)}.`,
  1233. );
  1234. }
  1235. }
  1236. /**
  1237. * If the next token is a given keyword, return "true" after advancing the lexer.
  1238. * Otherwise, do not change the parser state and return "false".
  1239. */
  1240. expectOptionalKeyword(value) {
  1241. const token = this._lexer.token;
  1242. if (token.kind === TokenKind.NAME && token.value === value) {
  1243. this.advanceLexer();
  1244. return true;
  1245. }
  1246. return false;
  1247. }
  1248. /**
  1249. * Helper function for creating an error when an unexpected lexed token is encountered.
  1250. */
  1251. unexpected(atToken) {
  1252. const token =
  1253. atToken !== null && atToken !== void 0 ? atToken : this._lexer.token;
  1254. return syntaxError(
  1255. this._lexer.source,
  1256. token.start,
  1257. `Unexpected ${getTokenDesc(token)}.`,
  1258. );
  1259. }
  1260. /**
  1261. * Returns a possibly empty list of parse nodes, determined by the parseFn.
  1262. * This list begins with a lex token of openKind and ends with a lex token of closeKind.
  1263. * Advances the parser to the next lex token after the closing token.
  1264. */
  1265. any(openKind, parseFn, closeKind) {
  1266. this.expectToken(openKind);
  1267. const nodes = [];
  1268. while (!this.expectOptionalToken(closeKind)) {
  1269. nodes.push(parseFn.call(this));
  1270. }
  1271. return nodes;
  1272. }
  1273. /**
  1274. * Returns a list of parse nodes, determined by the parseFn.
  1275. * It can be empty only if open token is missing otherwise it will always return non-empty list
  1276. * that begins with a lex token of openKind and ends with a lex token of closeKind.
  1277. * Advances the parser to the next lex token after the closing token.
  1278. */
  1279. optionalMany(openKind, parseFn, closeKind) {
  1280. if (this.expectOptionalToken(openKind)) {
  1281. const nodes = [];
  1282. do {
  1283. nodes.push(parseFn.call(this));
  1284. } while (!this.expectOptionalToken(closeKind));
  1285. return nodes;
  1286. }
  1287. return [];
  1288. }
  1289. /**
  1290. * Returns a non-empty list of parse nodes, determined by the parseFn.
  1291. * This list begins with a lex token of openKind and ends with a lex token of closeKind.
  1292. * Advances the parser to the next lex token after the closing token.
  1293. */
  1294. many(openKind, parseFn, closeKind) {
  1295. this.expectToken(openKind);
  1296. const nodes = [];
  1297. do {
  1298. nodes.push(parseFn.call(this));
  1299. } while (!this.expectOptionalToken(closeKind));
  1300. return nodes;
  1301. }
  1302. /**
  1303. * Returns a non-empty list of parse nodes, determined by the parseFn.
  1304. * This list may begin with a lex token of delimiterKind followed by items separated by lex tokens of tokenKind.
  1305. * Advances the parser to the next lex token after last item in the list.
  1306. */
  1307. delimitedMany(delimiterKind, parseFn) {
  1308. this.expectOptionalToken(delimiterKind);
  1309. const nodes = [];
  1310. do {
  1311. nodes.push(parseFn.call(this));
  1312. } while (this.expectOptionalToken(delimiterKind));
  1313. return nodes;
  1314. }
  1315. advanceLexer() {
  1316. const { maxTokens } = this._options;
  1317. const token = this._lexer.advance();
  1318. if (maxTokens !== undefined && token.kind !== TokenKind.EOF) {
  1319. ++this._tokenCounter;
  1320. if (this._tokenCounter > maxTokens) {
  1321. throw syntaxError(
  1322. this._lexer.source,
  1323. token.start,
  1324. `Document contains more that ${maxTokens} tokens. Parsing aborted.`,
  1325. );
  1326. }
  1327. }
  1328. }
  1329. }
  1330. /**
  1331. * A helper function to describe a token as a string for debugging.
  1332. */
  1333. function getTokenDesc(token) {
  1334. const value = token.value;
  1335. return getTokenKindDesc(token.kind) + (value != null ? ` "${value}"` : '');
  1336. }
  1337. /**
  1338. * A helper function to describe a token kind as a string for debugging.
  1339. */
  1340. function getTokenKindDesc(kind) {
  1341. return isPunctuatorTokenKind(kind) ? `"${kind}"` : kind;
  1342. }