Update GraphQL parser (#3605)

* Update GraphQL parser

Fixes #3601

This adds support for
- description as strings
- extending all the possible types
- block strings
- allow removing {} if there's no implementation for all graphql types

This is a breaking change but shouldn't be a big issue.
- Empty types are no longer allowed (there's an option to enable it but it hasn't been released yet). The fix is to remove `{}`

Something that hasn't been changed because not released:
- Doesn't support the new `&` separator for implementing multiple interfaces

A bug has been fixed:
- Now properly prints @directives for unions.

* Properly handle triple quotes
master
Christopher Chedeau 2017-12-31 16:45:06 +01:00 committed by GitHub
parent 51744e1008
commit 58e5536741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 571 additions and 108 deletions

View File

@ -33,7 +33,7 @@
"flow-parser": "0.59.0",
"get-stream": "3.0.0",
"globby": "6.1.0",
"graphql": "0.10.5",
"graphql": "0.12.3",
"ignore": "3.3.7",
"jest-docblock": "21.3.0-beta.11",
"jest-validate": "21.1.0",

View File

@ -31,9 +31,11 @@ function genericPrint(path, options, print) {
]);
}
case "OperationDefinition": {
const hasOperation = options.originalText[util.locStart(n)] !== "{";
const hasName = !!n.name;
return concat([
n.name === null ? "" : n.operation,
n.name ? concat([" ", path.call(print, "name")]) : "",
hasOperation ? n.operation : "",
hasOperation && hasName ? concat([" ", path.call(print, "name")]) : "",
n.variableDefinitions && n.variableDefinitions.length
? group(
concat([
@ -53,7 +55,7 @@ function genericPrint(path, options, print) {
)
: "",
printDirectives(path, print, n),
n.selectionSet ? (n.name === null ? "" : " ") : "",
n.selectionSet ? (!hasOperation && !hasName ? "" : " ") : "",
path.call(print, "selectionSet")
]);
}
@ -123,6 +125,11 @@ function genericPrint(path, options, print) {
return n.value;
}
case "StringValue": {
if (n.block) {
// It is not possible to distinguish between """\nabc\n""" and """abc"""
// https://github.com/graphql/graphql-js/issues/1188
return options.originalText.slice(util.locStart(n), util.locEnd(n));
}
return concat(['"', n.value.replace(/["\\]/g, "\\$&"), '"']);
}
case "IntValue":
@ -231,31 +238,37 @@ function genericPrint(path, options, print) {
return concat(["extend ", path.call(print, "definition")]);
}
case "ObjectTypeExtension":
case "ObjectTypeDefinition": {
return concat([
path.call(print, "description"),
n.description ? hardline : "",
n.kind === "ObjectTypeExtension" ? "extend " : "",
"type ",
path.call(print, "name"),
n.interfaces.length > 0
? concat([" implements ", join(", ", path.map(print, "interfaces"))])
: "",
printDirectives(path, print, n),
" {",
n.fields.length > 0
? indent(
concat([
hardline,
join(
? concat([
" {",
indent(
concat([
hardline,
path.call(
fieldsPath => printSequence(fieldsPath, options, print),
"fields"
join(
hardline,
path.call(
fieldsPath => printSequence(fieldsPath, options, print),
"fields"
)
)
)
])
)
: "",
hardline,
"}"
])
),
hardline,
"}"
])
: ""
]);
}
@ -291,6 +304,8 @@ function genericPrint(path, options, print) {
case "DirectiveDefinition": {
return concat([
path.call(print, "description"),
n.description ? hardline : "",
"directive ",
"@",
path.call(print, "name"),
@ -319,28 +334,35 @@ function genericPrint(path, options, print) {
]);
}
case "EnumTypeExtension":
case "EnumTypeDefinition": {
return concat([
path.call(print, "description"),
n.description ? hardline : "",
n.kind === "EnumTypeExtension" ? "extend " : "",
"enum ",
path.call(print, "name"),
printDirectives(path, print, n),
" {",
n.values.length > 0
? indent(
concat([
hardline,
join(
? concat([
" {",
indent(
concat([
hardline,
path.call(
valuesPath => printSequence(valuesPath, options, print),
"values"
join(
hardline,
path.call(
valuesPath => printSequence(valuesPath, options, print),
"values"
)
)
)
])
)
: "",
hardline,
"}"
])
),
hardline,
"}"
])
: ""
]);
}
@ -361,28 +383,34 @@ function genericPrint(path, options, print) {
]);
}
case "InputObjectTypeExtension":
case "InputObjectTypeDefinition": {
return concat([
path.call(print, "description"),
n.description ? hardline : "",
n.kind === "InputObjectTypeExtension" ? "extend " : "",
"input ",
path.call(print, "name"),
printDirectives(path, print, n),
" {",
n.fields.length > 0
? indent(
concat([
hardline,
join(
? concat([
" {",
indent(
concat([
hardline,
path.call(
fieldsPath => printSequence(fieldsPath, options, print),
"fields"
join(
hardline,
path.call(
fieldsPath => printSequence(fieldsPath, options, print),
"fields"
)
)
)
])
)
: "",
hardline,
"}"
])
),
hardline,
"}"
])
: ""
]);
}
@ -418,28 +446,35 @@ function genericPrint(path, options, print) {
]);
}
case "InterfaceTypeExtension":
case "InterfaceTypeDefinition": {
return concat([
path.call(print, "description"),
n.description ? hardline : "",
n.kind === "InterfaceTypeExtension" ? "extend " : "",
"interface ",
path.call(print, "name"),
printDirectives(path, print, n),
" {",
n.fields.length > 0
? indent(
concat([
hardline,
join(
? concat([
" {",
indent(
concat([
hardline,
path.call(
fieldsPath => printSequence(fieldsPath, options, print),
"fields"
join(
hardline,
path.call(
fieldsPath => printSequence(fieldsPath, options, print),
"fields"
)
)
)
])
)
: "",
hardline,
"}"
])
),
hardline,
"}"
])
: ""
]);
}
@ -463,25 +498,42 @@ function genericPrint(path, options, print) {
]);
}
case "UnionTypeExtension":
case "UnionTypeDefinition": {
return group(
concat([
"union ",
path.call(print, "name"),
" =",
ifBreak("", " "),
indent(
path.call(print, "description"),
n.description ? hardline : "",
group(
concat([
ifBreak(concat([line, " "])),
join(concat([line, "| "]), path.map(print, "types"))
n.kind === "UnionTypeExtension" ? "extend " : "",
"union ",
path.call(print, "name"),
printDirectives(path, print, n),
n.types.length > 0
? concat([
" =",
ifBreak("", " "),
indent(
concat([
ifBreak(concat([line, " "])),
join(concat([line, "| "]), path.map(print, "types"))
])
)
])
: ""
])
)
])
);
}
case "ScalarTypeExtension":
case "ScalarTypeDefinition": {
return concat([
path.call(print, "description"),
n.description ? hardline : "",
n.kind === "ScalarTypeExtension" ? "extend " : "",
"scalar ",
path.call(print, "name"),
printDirectives(path, print, n)

View File

@ -1,23 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`directive_decl.graphql 1`] = `
directive @a on FIELD1
directive @a(as: String) on FIELD1
directive @a(as: String = 1) on FIELD1
directive @a(as: String, b: Int!) on FIELD1
directive @a(as: String! = 1 @deprecated) on FIELD1
directive @a(as: String! = 1 @deprecated) on FIELD1 | FIELD2
directive @a on QUERY
directive @a(as: String) on QUERY
directive @a(as: String = 1) on QUERY
directive @a(as: String, b: Int!) on QUERY
directive @a(as: String! = 1 @deprecated) on QUERY
directive @a(as: String! = 1 @deprecated) on QUERY | MUTATION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
directive @a on FIELD1
directive @a on QUERY
directive @a(as: String) on FIELD1
directive @a(as: String) on QUERY
directive @a(as: String = 1) on FIELD1
directive @a(as: String = 1) on QUERY
directive @a(as: String, b: Int!) on FIELD1
directive @a(as: String, b: Int!) on QUERY
directive @a(as: String! = 1 @deprecated) on FIELD1
directive @a(as: String! = 1 @deprecated) on QUERY
directive @a(as: String! = 1 @deprecated) on FIELD1 | FIELD2
directive @a(as: String! = 1 @deprecated) on QUERY | MUTATION
`;

View File

@ -1,6 +1,6 @@
directive @a on FIELD1
directive @a(as: String) on FIELD1
directive @a(as: String = 1) on FIELD1
directive @a(as: String, b: Int!) on FIELD1
directive @a(as: String! = 1 @deprecated) on FIELD1
directive @a(as: String! = 1 @deprecated) on FIELD1 | FIELD2
directive @a on QUERY
directive @a(as: String) on QUERY
directive @a(as: String = 1) on QUERY
directive @a(as: String, b: Int!) on QUERY
directive @a(as: String! = 1 @deprecated) on QUERY
directive @a(as: String! = 1 @deprecated) on QUERY | MUTATION

View File

@ -1,6 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`kitchen_sink.graphql 1`] = `
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
query queryName($foo: ComplexType, $site: Site = MOBILE) {
whoever123is: node(id: [123, 456]) {
id ,
@ -44,18 +49,21 @@ subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
}
fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value"})
foo(size: $size, bar: $b, obj: {key: "value", block: """
block string uses \\"""
"""})
}
{
unnamed(truthy: true, falsey: false, nullish: null),
query
}
query {
field
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
query queryName($foo: ComplexType, $site: Site = MOBILE) {
whoever123is: node(id: [123, 456]) {
id
@ -99,7 +107,13 @@ subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
}
fragment frag on Friend {
foo(size: $size, bar: $b, obj: { key: "value" })
foo(
size: $size
bar: $b
obj: { key: "value", block: """
block string uses \\"""
""" }
)
}
{
@ -107,8 +121,243 @@ fragment frag on Friend {
query
}
query {
field
`;
exports[`schema_kitchen_sink.graphql 1`] = `
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
schema {
query: QueryType
mutation: MutationType
}
"""
This is a description
of the \`Foo\` type.
"""
type Foo implements Bar {
one: Type
two(argument: InputType!): Type
three(argument: InputType, other: String): Int
four(argument: String = "string"): String
five(argument: [String] = ["string", "string"]): String
six(argument: InputType = {key: "value"}): Type
seven(argument: Int = null): Type
}
type AnnotatedObject @onObject(arg: "value") {
annotatedField(arg: Type = "default" @onArg): Type @onField
}
type UndefinedType
extend type Foo {
seven(argument: [String]): Type
}
extend type Foo @onType
"""
This is a description
"""
interface Bar {
one: Type
four(argument: String = "string"): String
}
interface AnnotatedInterface @onInterface {
annotatedField(arg: Type @onArg): Type @onField
}
interface UndefinedInterface
extend interface Bar {
two(argument: InputType!): Type
}
extend interface Bar @onInterface
union Feed = Story | Article | Advert
union AnnotatedUnion @onUnion = A | B
union AnnotatedUnionTwo @onUnion = | A | B
union UndefinedUnion
extend union Feed = Photo | Video
extend union Feed @onUnion
scalar CustomScalar
scalar AnnotatedScalar @onScalar
extend scalar CustomScalar @onScalar
enum Site {
DESKTOP
MOBILE
}
enum AnnotatedEnum @onEnum {
ANNOTATED_VALUE @onEnumValue
OTHER_VALUE
}
enum UndefinedEnum
extend enum Site {
VR
}
extend enum Site @onEnum
input InputType {
key: String!
answer: Int = 42
}
input AnnotatedInput @onInputObject {
annotatedField: Type @onField
}
input UndefinedInput
extend input InputType {
other: Float = 1.23e4
}
extend input InputType @onInputObject
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @include(if: Boolean!)
on FIELD
| FRAGMENT_SPREAD
| INLINE_FRAGMENT
directive @include2(if: Boolean!) on
| FIELD
| FRAGMENT_SPREAD
| INLINE_FRAGMENT
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
schema {
query: QueryType
mutation: MutationType
}
"""
This is a description
of the \`Foo\` type.
"""
type Foo implements Bar {
one: Type
two(argument: InputType!): Type
three(argument: InputType, other: String): Int
four(argument: String = "string"): String
five(argument: [String] = ["string", "string"]): String
six(argument: InputType = { key: "value" }): Type
seven(argument: Int = null): Type
}
type AnnotatedObject @onObject(arg: "value") {
annotatedField(arg: Type = "default" @onArg): Type @onField
}
type UndefinedType
extend type Foo {
seven(argument: [String]): Type
}
extend type Foo @onType
"""
This is a description
"""
interface Bar {
one: Type
four(argument: String = "string"): String
}
interface AnnotatedInterface @onInterface {
annotatedField(arg: Type @onArg): Type @onField
}
interface UndefinedInterface
extend interface Bar {
two(argument: InputType!): Type
}
extend interface Bar @onInterface
union Feed = Story | Article | Advert
union AnnotatedUnion @onUnion = A | B
union AnnotatedUnionTwo @onUnion = A | B
union UndefinedUnion
extend union Feed = Photo | Video
extend union Feed @onUnion
scalar CustomScalar
scalar AnnotatedScalar @onScalar
extend scalar CustomScalar @onScalar
enum Site {
DESKTOP
MOBILE
}
enum AnnotatedEnum @onEnum {
ANNOTATED_VALUE @onEnumValue
OTHER_VALUE
}
enum UndefinedEnum
extend enum Site {
VR
}
extend enum Site @onEnum
input InputType {
key: String!
answer: Int = 42
}
input AnnotatedInput @onInputObject {
annotatedField: Type @onField
}
input UndefinedInput
extend input InputType {
other: Float = 1.23e4
}
extend input InputType @onInputObject
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @include2(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
`;

View File

@ -1,3 +1,8 @@
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
query queryName($foo: ComplexType, $site: Site = MOBILE) {
whoever123is: node(id: [123, 456]) {
id ,
@ -41,14 +46,12 @@ subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
}
fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value"})
foo(size: $size, bar: $b, obj: {key: "value", block: """
block string uses \"""
"""})
}
{
unnamed(truthy: true, falsey: false, nullish: null),
query
}
query {
field
}

View File

@ -0,0 +1,120 @@
# Copyright (c) 2015-present, Facebook, Inc.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
schema {
query: QueryType
mutation: MutationType
}
"""
This is a description
of the `Foo` type.
"""
type Foo implements Bar {
one: Type
two(argument: InputType!): Type
three(argument: InputType, other: String): Int
four(argument: String = "string"): String
five(argument: [String] = ["string", "string"]): String
six(argument: InputType = {key: "value"}): Type
seven(argument: Int = null): Type
}
type AnnotatedObject @onObject(arg: "value") {
annotatedField(arg: Type = "default" @onArg): Type @onField
}
type UndefinedType
extend type Foo {
seven(argument: [String]): Type
}
extend type Foo @onType
"""
This is a description
"""
interface Bar {
one: Type
four(argument: String = "string"): String
}
interface AnnotatedInterface @onInterface {
annotatedField(arg: Type @onArg): Type @onField
}
interface UndefinedInterface
extend interface Bar {
two(argument: InputType!): Type
}
extend interface Bar @onInterface
union Feed = Story | Article | Advert
union AnnotatedUnion @onUnion = A | B
union AnnotatedUnionTwo @onUnion = | A | B
union UndefinedUnion
extend union Feed = Photo | Video
extend union Feed @onUnion
scalar CustomScalar
scalar AnnotatedScalar @onScalar
extend scalar CustomScalar @onScalar
enum Site {
DESKTOP
MOBILE
}
enum AnnotatedEnum @onEnum {
ANNOTATED_VALUE @onEnumValue
OTHER_VALUE
}
enum UndefinedEnum
extend enum Site {
VR
}
extend enum Site @onEnum
input InputType {
key: String!
answer: Int = 42
}
input AnnotatedInput @onInputObject {
annotatedField: Type @onField
}
input UndefinedInput
extend input InputType {
other: Float = 1.23e4
}
extend input InputType @onInputObject
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @include(if: Boolean!)
on FIELD
| FRAGMENT_SPREAD
| INLINE_FRAGMENT
directive @include2(if: Boolean!) on
| FIELD
| FRAGMENT_SPREAD
| INLINE_FRAGMENT

View File

@ -10,7 +10,7 @@ directive @dir(
arg2: String
arg3: String
) on Field
) on QUERY
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
directive @dir(
# comment
@ -19,7 +19,7 @@ directive @dir(
# comment
arg2: String
arg3: String
) on Field
) on QUERY
`;

View File

@ -7,4 +7,4 @@ directive @dir(
arg2: String
arg3: String
) on Field
) on QUERY

View File

@ -35,9 +35,11 @@ extend type Feedback {
exports[`implements.graphql 1`] = `
type VRMConversation implements Node, Entity @foo {
a: Int
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type VRMConversation implements Node, Entity @foo {
a: Int
}
`;

View File

@ -1,2 +1,3 @@
type VRMConversation implements Node, Entity @foo {
a: Int
}

View File

@ -2,9 +2,33 @@
exports[`string.graphql 1`] = `
query X($a: Int) @relay(meta: "{\\"lowPri\\": true}") { a }
"""abc"""
type T {
a: Int
}
"""
abc
"""
type T {
a: Int
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
query X($a: Int) @relay(meta: "{\\"lowPri\\": true}") {
a
}
"""abc"""
type T {
a: Int
}
"""
abc
"""
type T {
a: Int
}
`;

View File

@ -1 +1,13 @@
query X($a: Int) @relay(meta: "{\"lowPri\": true}") { a }
"""abc"""
type T {
a: Int
}
"""
abc
"""
type T {
a: Int
}

View File

@ -2001,11 +2001,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
graphql@0.10.5:
version "0.10.5"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.10.5.tgz#c9be17ca2bdfdbd134077ffd9bbaa48b8becd298"
graphql@0.12.3:
version "0.12.3"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.12.3.tgz#11668458bbe28261c0dcb6e265f515ba79f6ce07"
dependencies:
iterall "^1.1.0"
iterall "1.1.3"
growly@^1.3.0:
version "1.3.0"
@ -2482,9 +2482,9 @@ istanbul-reports@^1.1.1:
dependencies:
handlebars "^4.0.3"
iterall@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.1.tgz#f7f0af11e9a04ec6426260f5019d9fcca4d50214"
iterall@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.3.tgz#1cbbff96204056dde6656e2ed2e2226d0e6d72c9"
jest-changed-files@^21.2.0:
version "21.2.0"