Skip to content

Commit

Permalink
Refine parse and AST to represent ConstValue
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
leebyron committed May 6, 2021
1 parent 9ba6b17 commit 0b36216
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 84 deletions.
8 changes: 8 additions & 0 deletions src/index.d.ts
Expand Up @@ -200,6 +200,7 @@ export {
// Parse
parse,
parseValue,
parseConstValue,
parseType,
// Print
print,
Expand All @@ -215,6 +216,7 @@ export {
isExecutableDefinitionNode,
isSelectionNode,
isValueNode,
isConstValueNode,
isTypeNode,
isTypeSystemDefinitionNode,
isTypeDefinitionNode,
Expand Down Expand Up @@ -247,20 +249,26 @@ export {
SelectionNode,
FieldNode,
ArgumentNode,
ConstArgumentNode,
FragmentSpreadNode,
InlineFragmentNode,
FragmentDefinitionNode,
ValueNode,
ConstValueNode,
IntValueNode,
FloatValueNode,
StringValueNode,
BooleanValueNode,
NullValueNode,
EnumValueNode,
ListValueNode,
ConstListValueNode,
ObjectValueNode,
ConstObjectValueNode,
ObjectFieldNode,
ConstObjectFieldNode,
DirectiveNode,
ConstDirectiveNode,
TypeNode,
NamedTypeNode,
ListTypeNode,
Expand Down
8 changes: 8 additions & 0 deletions src/index.js
Expand Up @@ -187,6 +187,7 @@ export {
// Parse
parse,
parseValue,
parseConstValue,
parseType,
// Print
print,
Expand All @@ -202,6 +203,7 @@ export {
isExecutableDefinitionNode,
isSelectionNode,
isValueNode,
isConstValueNode,
isTypeNode,
isTypeSystemDefinitionNode,
isTypeDefinitionNode,
Expand Down Expand Up @@ -234,20 +236,26 @@ export type {
SelectionNode,
FieldNode,
ArgumentNode,
ConstArgumentNode,
FragmentSpreadNode,
InlineFragmentNode,
FragmentDefinitionNode,
ValueNode,
ConstValueNode,
IntValueNode,
FloatValueNode,
StringValueNode,
BooleanValueNode,
NullValueNode,
EnumValueNode,
ListValueNode,
ConstListValueNode,
ObjectValueNode,
ConstObjectValueNode,
ObjectFieldNode,
ConstObjectFieldNode,
DirectiveNode,
ConstDirectiveNode,
TypeNode,
NamedTypeNode,
ListTypeNode,
Expand Down
92 changes: 90 additions & 2 deletions src/language/__tests__/parser-test.js
Expand Up @@ -9,7 +9,7 @@ import { inspect } from '../../jsutils/inspect';
import { Kind } from '../kinds';
import { Source } from '../source';
import { TokenKind } from '../tokenKind';
import { parse, parseValue, parseType } from '../parser';
import { parse, parseValue, parseConstValue, parseType } from '../parser';

import { toJSONDeep } from './toJSONDeep';

Expand Down Expand Up @@ -95,7 +95,7 @@ describe('Parser', () => {
expectSyntaxError(
'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }',
).to.deep.equal({
message: 'Syntax Error: Unexpected "$".',
message: 'Syntax Error: Unexpected variable "$var" in constant value.',
locations: [{ line: 1, column: 37 }],
});
});
Expand Down Expand Up @@ -447,6 +447,94 @@ describe('Parser', () => {
],
});
});

it('allows variables', () => {
const result = parseValue('{ field: $var }');
expect(toJSONDeep(result)).to.deep.equal({
kind: Kind.OBJECT,
loc: { start: 0, end: 15 },
fields: [
{
kind: Kind.OBJECT_FIELD,
loc: { start: 2, end: 13 },
name: {
kind: Kind.NAME,
loc: { start: 2, end: 7 },
value: 'field',
},
value: {
kind: Kind.VARIABLE,
loc: { start: 9, end: 13 },
name: {
kind: Kind.NAME,
loc: { start: 10, end: 13 },
value: 'var',
},
},
},
],
});
});

it('correct message for incomplete variable', () => {
expect(() => parseValue('$'))
.to.throw()
.to.deep.include({
message: 'Syntax Error: Expected Name, found <EOF>.',
locations: [{ line: 1, column: 2 }],
});
});

it('correct message for unexpected token', () => {
expect(() => parseValue(':'))
.to.throw()
.to.deep.include({
message: 'Syntax Error: Unexpected ":".',
locations: [{ line: 1, column: 1 }],
});
});
});

describe('parseConstValue', () => {
it('parses values', () => {
const result = parseConstValue('[123 "abc"]');
expect(toJSONDeep(result)).to.deep.equal({
kind: Kind.LIST,
loc: { start: 0, end: 11 },
values: [
{
kind: Kind.INT,
loc: { start: 1, end: 4 },
value: '123',
},
{
kind: Kind.STRING,
loc: { start: 5, end: 10 },
value: 'abc',
block: false,
},
],
});
});

it('does not allow variables', () => {
expect(() => parseConstValue('{ field: $var }'))
.to.throw()
.to.deep.include({
message:
'Syntax Error: Unexpected variable "$var" in constant value.',
locations: [{ line: 1, column: 10 }],
});
});

it('correct message for unexpected token', () => {
expect(() => parseConstValue('$'))
.to.throw()
.to.deep.include({
message: 'Syntax Error: Unexpected "$".',
locations: [{ line: 1, column: 1 }],
});
});
});

describe('parseType', () => {
Expand Down
13 changes: 13 additions & 0 deletions src/language/__tests__/predicates-test.js
Expand Up @@ -3,11 +3,13 @@ import { describe, it } from 'mocha';

import type { ASTNode } from '../ast';
import { Kind } from '../kinds';
import { parseValue } from '../parser';
import {
isDefinitionNode,
isExecutableDefinitionNode,
isSelectionNode,
isValueNode,
isConstValueNode,
isTypeNode,
isTypeSystemDefinitionNode,
isTypeDefinitionNode,
Expand Down Expand Up @@ -75,6 +77,17 @@ describe('AST node predicates', () => {
]);
});

it('isConstValueNode', () => {
expect(isConstValueNode(parseValue('"value"'))).to.equal(true);
expect(isConstValueNode(parseValue('$var'))).to.equal(false);

expect(isConstValueNode(parseValue('{ field: "value" }'))).to.equal(true);
expect(isConstValueNode(parseValue('{ field: $var }'))).to.equal(false);

expect(isConstValueNode(parseValue('[ "value" ]'))).to.equal(true);
expect(isConstValueNode(parseValue('[ $var ]'))).to.equal(false);
});

it('isTypeNode', () => {
expect(filterNodes(isTypeNode)).to.deep.equal([
'NamedType',
Expand Down

0 comments on commit 0b36216

Please sign in to comment.