Skip to content

Commit

Permalink
RFC: Descriptions as strings.
Browse files Browse the repository at this point in the history
As discussed in graphql/graphql-spec#90

This proposes replacing leading comment blocks as descriptions in the schema definition language with leading strings.

While I think there is some reduced ergonomics of using a string literal instead of a comment to write descriptions (unless perhaps you are accustomed to Python or Clojure), there are some compelling advantages:

* Descriptions are first-class in the AST of the schema definition language.
* Comments can remain "ignored" characters.
* No ambiguity between commented out regions and descriptions.

Specific to this reference implementation, since this is a breaking change and comment descriptions in the experimental SDL have fairly wide usage, I've left the comment description implementation intact and allow it to be enabled via an option. This should help with allowing upgrading with minimal impact on existing codebases and aid in automated transforms.
  • Loading branch information
leebyron committed Jun 22, 2017
1 parent ce2d30a commit e8b93ed
Show file tree
Hide file tree
Showing 14 changed files with 754 additions and 194 deletions.
4 changes: 4 additions & 0 deletions src/language/__tests__/schema-kitchen-sink.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ schema {
mutation: MutationType
}

"""
This is a description
of the `Foo` type.
"""
type Foo implements Bar {
one: Type
two(argument: InputType!): Type
Expand Down
58 changes: 58 additions & 0 deletions src/language/__tests__/schema-parser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,55 @@ type Hello {
expect(printJson(doc)).to.equal(printJson(expected));
});

it('parses type with description string', () => {
const doc = parse(`
"Description"
type Hello {
world: String
}`);
expect(doc).to.containSubset({
kind: 'Document',
definitions: [
{
kind: 'ObjectTypeDefinition',
name: nameNode('Hello', { start: 20, end: 25 }),
description: {
kind: 'StringValue',
value: 'Description',
loc: { start: 1, end: 14 },
}
}
],
loc: { start: 0, end: 45 },
});
});

it('parses type with description multi-line string', () => {
const doc = parse(`
"""
Description
"""
# Even with comments between them
type Hello {
world: String
}`);
expect(doc).to.containSubset({
kind: 'Document',
definitions: [
{
kind: 'ObjectTypeDefinition',
name: nameNode('Hello', { start: 60, end: 65 }),
description: {
kind: 'StringValue',
value: 'Description',
loc: { start: 1, end: 20 },
}
}
],
loc: { start: 0, end: 85 },
});
});

it('Simple extension', () => {
const body = `
extend type Hello {
Expand Down Expand Up @@ -130,6 +179,15 @@ extend type Hello {
expect(printJson(doc)).to.equal(printJson(expected));
});

it('Extension do not include descriptions', () => {
expect(() => parse(`
"Description"
extend type Hello {
world: String
}
`)).to.throw('Syntax Error GraphQL request (2:7)');
});

it('Simple non-null type', () => {
const body = `
type Hello {
Expand Down
4 changes: 4 additions & 0 deletions src/language/__tests__/schema-printer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ describe('Printer', () => {
mutation: MutationType
}
"""
This is a description
of the \`Foo\` type.
"""
type Foo implements Bar {
one: Type
two(argument: InputType!): Type
Expand Down
10 changes: 10 additions & 0 deletions src/language/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,13 +398,15 @@ export type TypeDefinitionNode =
export type ScalarTypeDefinitionNode = {
kind: 'ScalarTypeDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
directives?: ?Array<DirectiveNode>;
};

export type ObjectTypeDefinitionNode = {
kind: 'ObjectTypeDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
interfaces?: ?Array<NamedTypeNode>;
directives?: ?Array<DirectiveNode>;
Expand All @@ -414,6 +416,7 @@ export type ObjectTypeDefinitionNode = {
export type FieldDefinitionNode = {
kind: 'FieldDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
arguments: Array<InputValueDefinitionNode>;
type: TypeNode;
Expand All @@ -423,6 +426,7 @@ export type FieldDefinitionNode = {
export type InputValueDefinitionNode = {
kind: 'InputValueDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
type: TypeNode;
defaultValue?: ?ValueNode;
Expand All @@ -432,6 +436,7 @@ export type InputValueDefinitionNode = {
export type InterfaceTypeDefinitionNode = {
kind: 'InterfaceTypeDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
directives?: ?Array<DirectiveNode>;
fields: Array<FieldDefinitionNode>;
Expand All @@ -440,6 +445,7 @@ export type InterfaceTypeDefinitionNode = {
export type UnionTypeDefinitionNode = {
kind: 'UnionTypeDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
directives?: ?Array<DirectiveNode>;
types: Array<NamedTypeNode>;
Expand All @@ -448,6 +454,7 @@ export type UnionTypeDefinitionNode = {
export type EnumTypeDefinitionNode = {
kind: 'EnumTypeDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
directives?: ?Array<DirectiveNode>;
values: Array<EnumValueDefinitionNode>;
Expand All @@ -456,13 +463,15 @@ export type EnumTypeDefinitionNode = {
export type EnumValueDefinitionNode = {
kind: 'EnumValueDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
directives?: ?Array<DirectiveNode>;
};

export type InputObjectTypeDefinitionNode = {
kind: 'InputObjectTypeDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
directives?: ?Array<DirectiveNode>;
fields: Array<InputValueDefinitionNode>;
Expand All @@ -477,6 +486,7 @@ export type TypeExtensionDefinitionNode = {
export type DirectiveDefinitionNode = {
kind: 'DirectiveDefinition';
loc?: Location;
description?: ?StringValueNode;
name: NameNode;
arguments?: ?Array<InputValueDefinitionNode>;
locations: Array<NameNode>;
Expand Down
20 changes: 16 additions & 4 deletions src/language/lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,24 @@ export function createLexer<TOptions>(
token: startOfFileToken,
line: 1,
lineStart: 0,
advance: advanceLexer
advance: advanceLexer,
lookahead
};
return lexer;
}

function advanceLexer() {
let token = this.lastToken = this.token;
this.lastToken = this.token;
const token = this.token = this.lookahead();
return token;
}

function lookahead() {
let token = this.token;
if (token.kind !== EOF) {
do {
token = token.next = readToken(this, token);
token = token.next || (token.next = readToken(this, token));
} while (token.kind === COMMENT);
this.token = token;
}
return token;
}
Expand Down Expand Up @@ -80,6 +86,12 @@ export type Lexer<TOptions> = {
* Advances the token stream to the next non-ignored token.
*/
advance(): Token;

/**
* Looks ahead and returns the next non-ignored token, but does not change
* the Lexer's state.
*/
lookahead(): Token;
};

// Each kind of token.
Expand Down
Loading

0 comments on commit e8b93ed

Please sign in to comment.