diff --git a/src/language/parser.js b/src/language/parser.js index 4b11f2d07c..64b24ca778 100644 --- a/src/language/parser.js +++ b/src/language/parser.js @@ -311,7 +311,7 @@ function parseVariableValue(parser): Value { return parseValue(parser, false); } -function parseConstValue(parser): Value { +export function parseConstValue(parser): Value { return parseValue(parser, true); } diff --git a/src/language/schema/__tests__/parser.js b/src/language/schema/__tests__/parser.js index d4917e50a8..7521696536 100644 --- a/src/language/schema/__tests__/parser.js +++ b/src/language/schema/__tests__/parser.js @@ -50,8 +50,16 @@ function fieldNodeWithArgs(name, type, args, loc) { return { kind: 'FieldDefinition', name: name, - type: type, arguments: args, + type: type, + loc: loc, + }; +} + +function enumValueNode(name, loc) { + return { + kind: 'EnumValueDefinition', + name: nameNode(name, loc), loc: loc, }; } @@ -92,7 +100,7 @@ type Hello { ], loc: loc(1, 31), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple non-null type', () => { @@ -125,7 +133,7 @@ type Hello { ], loc: loc(1, 32), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); @@ -172,14 +180,6 @@ type Hello { expect(printJson(doc)).to.equal(printJson(expected)); }); - function enumValueNode(name, loc) { - return { - kind: 'EnumValueDefinition', - name: nameNode(name, loc), - loc: loc, - }; - } - it('Single value enum', () => { var body = `enum Hello { WORLD }`; var loc = createLocFn(body); @@ -246,7 +246,7 @@ interface Hello { ], loc: loc(1, 36), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple field with arg', () => { @@ -272,6 +272,7 @@ type Hello { kind: 'ArgumentDefinition', name: nameNode('flag', loc(22, 26)), type: typeNode('Boolean', loc(28, 35)), + defaultValue: null, loc: loc(22, 35), } ], @@ -283,7 +284,49 @@ type Hello { ], loc: loc(1, 46), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); + }); + + it('Simple field with arg with default value', () => { + var body = ` +type Hello { + world(flag: Boolean = true): String +}`; + var doc = parseSchema(body); + var loc = createLocFn(body); + var expected = { + kind: 'SchemaDocument', + definitions: [ + { + kind: 'TypeDefinition', + name: nameNode('Hello', loc(6, 11)), + interfaces: [], + fields: [ + fieldNodeWithArgs( + nameNode('world', loc(16, 21)), + typeNode('String', loc(45, 51)), + [ + { + kind: 'ArgumentDefinition', + name: nameNode('flag', loc(22, 26)), + type: typeNode('Boolean', loc(28, 35)), + defaultValue: { + kind: 'BooleanValue', + value: true, + loc: loc(38, 42), + }, + loc: loc(22, 42), + } + ], + loc(16, 51) + ) + ], + loc: loc(1, 53), + } + ], + loc: loc(1, 53), + }; + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple field with list arg', () => { @@ -313,6 +356,7 @@ type Hello { type: typeNode('String', loc(31, 37)), loc: loc(30, 38) }, + defaultValue: null, loc: loc(22, 38), } ], @@ -324,7 +368,7 @@ type Hello { ], loc: loc(1, 49), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple field with two args', () => { @@ -350,12 +394,14 @@ type Hello { kind: 'ArgumentDefinition', name: nameNode('argOne', loc(22, 28)), type: typeNode('Boolean', loc(30, 37)), + defaultValue: null, loc: loc(22, 37), }, { kind: 'ArgumentDefinition', name: nameNode('argTwo', loc(39, 45)), type: typeNode('Int', loc(47, 50)), + defaultValue: null, loc: loc(39, 50), }, ], @@ -367,11 +413,11 @@ type Hello { ], loc: loc(1, 61), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple union', () => { - var body = `union Hello { World }`; + var body = `union Hello = World`; var doc = parseSchema(body); var loc = createLocFn(body); var expected = { @@ -381,16 +427,16 @@ type Hello { kind: 'UnionDefinition', name: nameNode('Hello', loc(6, 11)), types: [typeNode('World', loc(14, 19))], - loc: loc(0, 21), + loc: loc(0, 19), } ], - loc: loc(0, 21), + loc: loc(0, 19), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Union with two types', () => { - var body = `union Hello { Wo | Rld }`; + var body = `union Hello = Wo | Rld`; var doc = parseSchema(body); var loc = createLocFn(body); var expected = { @@ -403,12 +449,12 @@ type Hello { typeNode('Wo', loc(14, 16)), typeNode('Rld', loc(19, 22)), ], - loc: loc(0, 24), + loc: loc(0, 22), } ], - loc: loc(0, 24), + loc: loc(0, 22), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Scalar', () => { @@ -426,7 +472,7 @@ type Hello { ], loc: loc(0, 12), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple input object', () => { @@ -454,7 +500,7 @@ input Hello { ], loc: loc(1, 32), }; - expect(printJson(expected)).to.equal(printJson(doc)); + expect(printJson(doc)).to.equal(printJson(expected)); }); it('Simple input object with args should fail', () => { diff --git a/src/language/schema/ast.js b/src/language/schema/ast.js index 591acc04dd..f861ab98a2 100644 --- a/src/language/schema/ast.js +++ b/src/language/schema/ast.js @@ -11,6 +11,7 @@ import type { Location, Name, + Value, Type, NamedType } from '../ast'; @@ -41,8 +42,8 @@ export type FieldDefinition = { kind: 'FieldDefinition'; loc?: ?Location; name: Name; - type: Type; arguments: Array; + type: Type; } export type ArgumentDefinition = { @@ -50,6 +51,7 @@ export type ArgumentDefinition = { loc?: ?Location; name: Name; type: Type; + defaultValue?: ?Value; } export type InterfaceDefinition = { diff --git a/src/language/schema/parser.js b/src/language/schema/parser.js index c45c0ab3a9..12ad7afc7c 100644 --- a/src/language/schema/parser.js +++ b/src/language/schema/parser.js @@ -28,6 +28,7 @@ import { import { parseName, + parseConstValue, parseType, parseNamedType, } from '../parser'; @@ -72,6 +73,9 @@ export function parseSchema( return parseSchemaDocument(parser); } +/** + * SchemaDocument : SchemaDefinition+ + */ function parseSchemaDocument(parser): SchemaDocument { var start = parser.token.start; var definitions = []; @@ -86,6 +90,15 @@ function parseSchemaDocument(parser): SchemaDocument { }; } +/** + * SchemaDefinition : + * - TypeDefinition + * - InterfaceDefinition + * - UnionDefinition + * - ScalarDefinition + * - EnumDefinition + * - InputObjectDefinition + */ function parseSchemaDefinition(parser): SchemaDefinition { if (!peek(parser, TokenKind.NAME)) { throw unexpected(parser); @@ -108,26 +121,35 @@ function parseSchemaDefinition(parser): SchemaDefinition { } } +/** + * TypeDefinition : TypeName ImplementsInterfaces? { FieldDefinition+ } + * + * TypeName : Name + */ function parseTypeDefinition(parser): TypeDefinition { var start = parser.token.start; expectKeyword(parser, 'type'); var name = parseName(parser); - var interfaces = parseInterfaces(parser); + var interfaces = parseImplementsInterfaces(parser); var fields = any( parser, TokenKind.BRACE_L, parseFieldDefinition, - TokenKind.BRACE_R); + TokenKind.BRACE_R + ); return { kind: TYPE_DEFINITION, name, - interfaces: interfaces, - fields: fields, + interfaces, + fields, loc: loc(parser, start), }; } -function parseInterfaces(parser): Array { +/** + * ImplementsInterfaces : `implements` NamedType+ + */ +function parseImplementsInterfaces(parser): Array { var types = []; if (parser.token.value === 'implements') { advance(parser); @@ -138,48 +160,64 @@ function parseInterfaces(parser): Array { return types; } +/** + * FieldDefinition : FieldName ArgumentsDefinition? : Type + * + * FieldName : Name + */ function parseFieldDefinition(parser): FieldDefinition { var start = parser.token.start; var name = parseName(parser); var args = parseArgumentDefs(parser); expect(parser, TokenKind.COLON); var type = parseType(parser); - var location = loc(parser, start); return { kind: FIELD_DEFINITION, - name: name, - type: type, + name, arguments: args, - loc: location, + type, + loc: loc(parser, start), }; } +/** + * ArgumentsDefinition : ( ArgumentDefinition+ ) + */ function parseArgumentDefs(parser): Array { if (!peek(parser, TokenKind.PAREN_L)) { return []; } - return many( - parser, - TokenKind.PAREN_L, - parseArgumentDef, - TokenKind.PAREN_R - ); + return many(parser, TokenKind.PAREN_L, parseArgumentDef, TokenKind.PAREN_R); } +/** + * ArgumentDefinition : ArgumentName : Value[Const] DefaultValue? + * + * ArgumentName : Name + * + * DefaultValue : = Value[Const] + */ function parseArgumentDef(parser): ArgumentDefinition { var start = parser.token.start; var name = parseName(parser); expect(parser, TokenKind.COLON); var type = parseType(parser, false); - var location = loc(parser, start); + var defaultValue = null; + if (skip(parser, TokenKind.EQUALS)) { + defaultValue = parseConstValue(parser); + } return { kind: ARGUMENT_DEFINITION, - name: name, - type: type, - loc: location, + name, + type, + defaultValue, + loc: loc(parser, start), }; } +/** + * InterfaceDefinition : `interface` TypeName { Fields+ } + */ function parseInterfaceDefinition(parser): InterfaceDefinition { var start = parser.token.start; expectKeyword(parser, 'interface'); @@ -188,51 +226,63 @@ function parseInterfaceDefinition(parser): InterfaceDefinition { parser, TokenKind.BRACE_L, parseFieldDefinition, - TokenKind.BRACE_R); + TokenKind.BRACE_R + ); return { kind: INTERFACE_DEFINITION, name, - fields: fields, + fields, loc: loc(parser, start), }; } +/** + * UnionDefinition : `union` TypeName = UnionMembers + */ function parseUnionDefinition(parser): UnionDefinition { var start = parser.token.start; expectKeyword(parser, 'union'); var name = parseName(parser); + expect(parser, TokenKind.EQUALS); var types = parseUnionMembers(parser); - var location = loc(parser, start); return { kind: UNION_DEFINITION, - name: name, - types: types, - loc: location, + name, + types, + loc: loc(parser, start), }; } -function parseUnionMembers(parser) { - expect(parser, TokenKind.BRACE_L); - var members = [(parseNamedType(parser))]; - while (!skip(parser, TokenKind.BRACE_R)) { - expect(parser, TokenKind.PIPE); - members.push((parseNamedType(parser))); - } +/** + * UnionMembers : + * - NamedType + * - UnionMembers | NamedType + */ +function parseUnionMembers(parser): Array { + var members = []; + do { + members.push(parseNamedType(parser)); + } while (skip(parser, TokenKind.PIPE)); return members; } +/** + * ScalarDefinition : `scalar` TypeName + */ function parseScalarDefinition(parser): ScalarDefinition { var start = parser.token.start; expectKeyword(parser, 'scalar'); var name = parseName(parser); - var location = loc(parser, start); return { kind: SCALAR_DEFINITION, - name: name, - loc: location, + name, + loc: loc(parser, start), }; } +/** + * EnumDefinition : `enum` TypeName { EnumValueDefinition+ } + */ function parseEnumDefinition(parser): EnumDefinition { var start = parser.token.start; expectKeyword(parser, 'enum'); @@ -241,27 +291,34 @@ function parseEnumDefinition(parser): EnumDefinition { parser, TokenKind.BRACE_L, parseEnumValueDefinition, - TokenKind.BRACE_R); - var location = loc(parser, start); + TokenKind.BRACE_R + ); return { kind: ENUM_DEFINITION, - name: name, - values: values, - loc: location, + name, + values, + loc: loc(parser, start), }; } +/** + * EnumValueDefinition : EnumValue + * + * EnumValue : Name + */ function parseEnumValueDefinition(parser) : EnumValueDefinition { var start = parser.token.start; var name = parseName(parser); - var location = loc(parser, start); return { kind: ENUM_VALUE_DEFINITION, - name: name, - loc: location, + name, + loc: loc(parser, start), }; } +/** + * InputObjectDefinition : `input` TypeName { InputFieldDefinition+ } + */ function parseInputObjectDefinition(parser): InputObjectDefinition { var start = parser.token.start; expectKeyword(parser, 'input'); @@ -270,25 +327,28 @@ function parseInputObjectDefinition(parser): InputObjectDefinition { parser, TokenKind.BRACE_L, parseInputFieldDefinition, - TokenKind.BRACE_R); + TokenKind.BRACE_R + ); return { kind: INPUT_OBJECT_DEFINITION, name, - fields: fields, + fields, loc: loc(parser, start), }; } +/** + * InputFieldDefinition : FieldName : Type + */ function parseInputFieldDefinition(parser): InputFieldDefinition { var start = parser.token.start; var name = parseName(parser); expect(parser, TokenKind.COLON); var type = parseType(parser); - var location = loc(parser, start); return { kind: INPUT_FIELD_DEFINITION, - name: name, - type: type, - loc: location, + name, + type, + loc: loc(parser, start), }; }