Skip to content

Commit 513eacc

Browse files
authored
Refine parse and AST to represent ConstValue (#3059)
This adds: * ConstValueNode - A subtype of ValueNode which recursively excludes Variables * Improved syntax error when encountering variable in a const value * parseConstValue(): ConstValueNode * isConstValue(): ConstValueNode * Various refinements to AST types to use ConstValueNode (or a type which includes it) to better align to the spec.
1 parent 5579180 commit 513eacc

File tree

12 files changed

+369
-77
lines changed

12 files changed

+369
-77
lines changed

src/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export {
200200
// Parse
201201
parse,
202202
parseValue,
203+
parseConstValue,
203204
parseType,
204205
// Print
205206
print,
@@ -215,6 +216,7 @@ export {
215216
isExecutableDefinitionNode,
216217
isSelectionNode,
217218
isValueNode,
219+
isConstValueNode,
218220
isTypeNode,
219221
isTypeSystemDefinitionNode,
220222
isTypeDefinitionNode,
@@ -247,20 +249,26 @@ export {
247249
SelectionNode,
248250
FieldNode,
249251
ArgumentNode,
252+
ConstArgumentNode,
250253
FragmentSpreadNode,
251254
InlineFragmentNode,
252255
FragmentDefinitionNode,
253256
ValueNode,
257+
ConstValueNode,
254258
IntValueNode,
255259
FloatValueNode,
256260
StringValueNode,
257261
BooleanValueNode,
258262
NullValueNode,
259263
EnumValueNode,
260264
ListValueNode,
265+
ConstListValueNode,
261266
ObjectValueNode,
267+
ConstObjectValueNode,
262268
ObjectFieldNode,
269+
ConstObjectFieldNode,
263270
DirectiveNode,
271+
ConstDirectiveNode,
264272
TypeNode,
265273
NamedTypeNode,
266274
ListTypeNode,

src/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export {
187187
// Parse
188188
parse,
189189
parseValue,
190+
parseConstValue,
190191
parseType,
191192
// Print
192193
print,
@@ -202,6 +203,7 @@ export {
202203
isExecutableDefinitionNode,
203204
isSelectionNode,
204205
isValueNode,
206+
isConstValueNode,
205207
isTypeNode,
206208
isTypeSystemDefinitionNode,
207209
isTypeDefinitionNode,
@@ -234,20 +236,26 @@ export type {
234236
SelectionNode,
235237
FieldNode,
236238
ArgumentNode,
239+
ConstArgumentNode,
237240
FragmentSpreadNode,
238241
InlineFragmentNode,
239242
FragmentDefinitionNode,
240243
ValueNode,
244+
ConstValueNode,
241245
IntValueNode,
242246
FloatValueNode,
243247
StringValueNode,
244248
BooleanValueNode,
245249
NullValueNode,
246250
EnumValueNode,
247251
ListValueNode,
252+
ConstListValueNode,
248253
ObjectValueNode,
254+
ConstObjectValueNode,
249255
ObjectFieldNode,
256+
ConstObjectFieldNode,
250257
DirectiveNode,
258+
ConstDirectiveNode,
251259
TypeNode,
252260
NamedTypeNode,
253261
ListTypeNode,

src/language/__tests__/parser-test.js

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { inspect } from '../../jsutils/inspect';
99
import { Kind } from '../kinds';
1010
import { Source } from '../source';
1111
import { TokenKind } from '../tokenKind';
12-
import { parse, parseValue, parseType } from '../parser';
12+
import { parse, parseValue, parseConstValue, parseType } from '../parser';
1313

1414
import { toJSONDeep } from './toJSONDeep';
1515

@@ -95,7 +95,7 @@ describe('Parser', () => {
9595
expectSyntaxError(
9696
'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }',
9797
).to.deep.equal({
98-
message: 'Syntax Error: Unexpected "$".',
98+
message: 'Syntax Error: Unexpected variable "$var" in constant value.',
9999
locations: [{ line: 1, column: 37 }],
100100
});
101101
});
@@ -447,6 +447,94 @@ describe('Parser', () => {
447447
],
448448
});
449449
});
450+
451+
it('allows variables', () => {
452+
const result = parseValue('{ field: $var }');
453+
expect(toJSONDeep(result)).to.deep.equal({
454+
kind: Kind.OBJECT,
455+
loc: { start: 0, end: 15 },
456+
fields: [
457+
{
458+
kind: Kind.OBJECT_FIELD,
459+
loc: { start: 2, end: 13 },
460+
name: {
461+
kind: Kind.NAME,
462+
loc: { start: 2, end: 7 },
463+
value: 'field',
464+
},
465+
value: {
466+
kind: Kind.VARIABLE,
467+
loc: { start: 9, end: 13 },
468+
name: {
469+
kind: Kind.NAME,
470+
loc: { start: 10, end: 13 },
471+
value: 'var',
472+
},
473+
},
474+
},
475+
],
476+
});
477+
});
478+
479+
it('correct message for incomplete variable', () => {
480+
expect(() => parseValue('$'))
481+
.to.throw()
482+
.to.deep.include({
483+
message: 'Syntax Error: Expected Name, found <EOF>.',
484+
locations: [{ line: 1, column: 2 }],
485+
});
486+
});
487+
488+
it('correct message for unexpected token', () => {
489+
expect(() => parseValue(':'))
490+
.to.throw()
491+
.to.deep.include({
492+
message: 'Syntax Error: Unexpected ":".',
493+
locations: [{ line: 1, column: 1 }],
494+
});
495+
});
496+
});
497+
498+
describe('parseConstValue', () => {
499+
it('parses values', () => {
500+
const result = parseConstValue('[123 "abc"]');
501+
expect(toJSONDeep(result)).to.deep.equal({
502+
kind: Kind.LIST,
503+
loc: { start: 0, end: 11 },
504+
values: [
505+
{
506+
kind: Kind.INT,
507+
loc: { start: 1, end: 4 },
508+
value: '123',
509+
},
510+
{
511+
kind: Kind.STRING,
512+
loc: { start: 5, end: 10 },
513+
value: 'abc',
514+
block: false,
515+
},
516+
],
517+
});
518+
});
519+
520+
it('does not allow variables', () => {
521+
expect(() => parseConstValue('{ field: $var }'))
522+
.to.throw()
523+
.to.deep.include({
524+
message:
525+
'Syntax Error: Unexpected variable "$var" in constant value.',
526+
locations: [{ line: 1, column: 10 }],
527+
});
528+
});
529+
530+
it('correct message for unexpected token', () => {
531+
expect(() => parseConstValue('$'))
532+
.to.throw()
533+
.to.deep.include({
534+
message: 'Syntax Error: Unexpected "$".',
535+
locations: [{ line: 1, column: 1 }],
536+
});
537+
});
450538
});
451539

452540
describe('parseType', () => {

src/language/__tests__/predicates-test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { describe, it } from 'mocha';
33

44
import type { ASTNode } from '../ast';
55
import { Kind } from '../kinds';
6+
import { parseValue } from '../parser';
67
import {
78
isDefinitionNode,
89
isExecutableDefinitionNode,
910
isSelectionNode,
1011
isValueNode,
12+
isConstValueNode,
1113
isTypeNode,
1214
isTypeSystemDefinitionNode,
1315
isTypeDefinitionNode,
@@ -75,6 +77,17 @@ describe('AST node predicates', () => {
7577
]);
7678
});
7779

80+
it('isConstValueNode', () => {
81+
expect(isConstValueNode(parseValue('"value"'))).to.equal(true);
82+
expect(isConstValueNode(parseValue('$var'))).to.equal(false);
83+
84+
expect(isConstValueNode(parseValue('{ field: "value" }'))).to.equal(true);
85+
expect(isConstValueNode(parseValue('{ field: $var }'))).to.equal(false);
86+
87+
expect(isConstValueNode(parseValue('[ "value" ]'))).to.equal(true);
88+
expect(isConstValueNode(parseValue('[ $var ]'))).to.equal(false);
89+
});
90+
7891
it('isTypeNode', () => {
7992
expect(filterNodes(isTypeNode)).to.deep.equal([
8093
'NamedType',

0 commit comments

Comments
 (0)