Skip to content

Commit 87e867e

Browse files
authored
Merge pull request #506 from graphql/parse-type
Adds parseType()
2 parents a0c25e5 + 73bf911 commit 87e867e

File tree

4 files changed

+123
-8
lines changed

4 files changed

+123
-8
lines changed

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export {
112112
// Parse
113113
parse,
114114
parseValue,
115+
parseType,
115116

116117
// Print
117118
print,

src/language/__tests__/parser-test.js

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import * as Kind from '../kinds';
1111
import { expect } from 'chai';
1212
import { describe, it } from 'mocha';
13-
import { parse } from '../parser';
13+
import { parse, parseValue, parseType } from '../parser';
1414
import { Source } from '../source';
1515
import { readFileSync } from 'fs';
1616
import { join } from 'path';
@@ -293,4 +293,93 @@ fragment ${fragmentName} on Type {
293293
expect(result.loc.startToken.kind).to.equal('<SOF>');
294294
expect(result.loc.endToken.kind).to.equal('<EOF>');
295295
});
296+
297+
describe('parseValue', () => {
298+
299+
it('parses list values', () => {
300+
expect(parseValue('[123 "abc"]')).to.containSubset({
301+
kind: Kind.LIST,
302+
loc: { start: 0, end: 11 },
303+
values: [
304+
{ kind: Kind.INT,
305+
loc: { start: 1, end: 4},
306+
value: '123' },
307+
{ kind: Kind.STRING,
308+
loc: { start: 5, end: 10},
309+
value: 'abc' } ]
310+
});
311+
});
312+
313+
});
314+
315+
describe('parseType', () => {
316+
317+
it('parses well known types', () => {
318+
expect(parseType('String')).to.containSubset({
319+
kind: Kind.NAMED_TYPE,
320+
loc: { start: 0, end: 6 },
321+
name: {
322+
kind: Kind.NAME,
323+
loc: { start: 0, end: 6 },
324+
value: 'String' }
325+
});
326+
});
327+
328+
it('parses custom types', () => {
329+
expect(parseType('MyType')).to.containSubset({
330+
kind: Kind.NAMED_TYPE,
331+
loc: { start: 0, end: 6 },
332+
name: {
333+
kind: Kind.NAME,
334+
loc: { start: 0, end: 6 },
335+
value: 'MyType' }
336+
});
337+
});
338+
339+
it('parses list types', () => {
340+
expect(parseType('[MyType]')).to.containSubset({
341+
kind: Kind.LIST_TYPE,
342+
loc: { start: 0, end: 8 },
343+
type: {
344+
kind: Kind.NAMED_TYPE,
345+
loc: { start: 1, end: 7 },
346+
name: {
347+
kind: Kind.NAME,
348+
loc: { start: 1, end: 7 },
349+
value: 'MyType' } }
350+
});
351+
});
352+
353+
it('parses non-null types', () => {
354+
expect(parseType('MyType!')).to.containSubset({
355+
kind: Kind.NON_NULL_TYPE,
356+
loc: { start: 0, end: 7 },
357+
type: {
358+
kind: Kind.NAMED_TYPE,
359+
loc: { start: 0, end: 6 },
360+
name: {
361+
kind: Kind.NAME,
362+
loc: { start: 0, end: 6 },
363+
value: 'MyType' } }
364+
});
365+
});
366+
367+
it('parses nested types', () => {
368+
expect(parseType('[MyType!]')).to.containSubset({
369+
kind: Kind.LIST_TYPE,
370+
loc: { start: 0, end: 9 },
371+
type: {
372+
kind: Kind.NON_NULL_TYPE,
373+
loc: { start: 1, end: 8 },
374+
type: {
375+
kind: Kind.NAMED_TYPE,
376+
loc: { start: 1, end: 7 },
377+
name: {
378+
kind: Kind.NAME,
379+
loc: { start: 1, end: 7 },
380+
value: 'MyType' } } }
381+
});
382+
});
383+
384+
});
296385
});

src/language/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export { getLocation } from './location';
1212
import * as Kind from './kinds';
1313
export { Kind };
1414
export { createLexer, TokenKind } from './lexer';
15-
export { parse, parseValue } from './parser';
15+
export { parse, parseValue, parseType } from './parser';
1616
export { print } from './printer';
1717
export { Source } from './source';
1818
export { visit, visitInParallel, visitWithTypeInfo, BREAK } from './visitor';

src/language/parser.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,14 @@ export function parse(
145145
}
146146

147147
/**
148-
* Given a string containing a GraphQL value, parse the AST for that value.
148+
* Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
149+
* that value.
149150
* Throws GraphQLError if a syntax error is encountered.
150151
*
151152
* This is useful within tools that operate upon GraphQL Values directly and
152153
* in isolation of complete GraphQL documents.
154+
*
155+
* Consider providing the results to the utility function: valueFromAST().
153156
*/
154157
export function parseValue(
155158
source: string | Source,
@@ -163,6 +166,28 @@ export function parseValue(
163166
return value;
164167
}
165168

169+
/**
170+
* Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
171+
* that type.
172+
* Throws GraphQLError if a syntax error is encountered.
173+
*
174+
* This is useful within tools that operate upon GraphQL Types directly and
175+
* in isolation of complete GraphQL documents.
176+
*
177+
* Consider providing the results to the utility function: typeFromAST().
178+
*/
179+
export function parseType(
180+
source: string | Source,
181+
options?: ParseOptions
182+
): Type {
183+
const sourceObj = typeof source === 'string' ? new Source(source) : source;
184+
const lexer = createLexer(sourceObj, options || {});
185+
expect(lexer, TokenKind.SOF);
186+
const type = parseTypeReference(lexer);
187+
expect(lexer, TokenKind.EOF);
188+
return type;
189+
}
190+
166191
/**
167192
* Converts a name lex token into a name parse node.
168193
*/
@@ -306,7 +331,7 @@ function parseVariableDefinition(lexer: Lexer<*>): VariableDefinition {
306331
return {
307332
kind: VARIABLE_DEFINITION,
308333
variable: parseVariable(lexer),
309-
type: (expect(lexer, TokenKind.COLON), parseType(lexer)),
334+
type: (expect(lexer, TokenKind.COLON), parseTypeReference(lexer)),
310335
defaultValue:
311336
skip(lexer, TokenKind.EQUALS) ? parseValueLiteral(lexer, true) : null,
312337
loc: loc(lexer, start)
@@ -633,11 +658,11 @@ function parseDirective(lexer: Lexer<*>): Directive {
633658
* - ListType
634659
* - NonNullType
635660
*/
636-
export function parseType(lexer: Lexer<*>): Type {
661+
export function parseTypeReference(lexer: Lexer<*>): Type {
637662
const start = lexer.token;
638663
let type;
639664
if (skip(lexer, TokenKind.BRACKET_L)) {
640-
type = parseType(lexer);
665+
type = parseTypeReference(lexer);
641666
expect(lexer, TokenKind.BRACKET_R);
642667
type = ({
643668
kind: LIST_TYPE,
@@ -807,7 +832,7 @@ function parseFieldDefinition(lexer: Lexer<*>): FieldDefinition {
807832
const name = parseName(lexer);
808833
const args = parseArgumentDefs(lexer);
809834
expect(lexer, TokenKind.COLON);
810-
const type = parseType(lexer);
835+
const type = parseTypeReference(lexer);
811836
const directives = parseDirectives(lexer);
812837
return {
813838
kind: FIELD_DEFINITION,
@@ -836,7 +861,7 @@ function parseInputValueDef(lexer: Lexer<*>): InputValueDefinition {
836861
const start = lexer.token;
837862
const name = parseName(lexer);
838863
expect(lexer, TokenKind.COLON);
839-
const type = parseType(lexer);
864+
const type = parseTypeReference(lexer);
840865
let defaultValue = null;
841866
if (skip(lexer, TokenKind.EQUALS)) {
842867
defaultValue = parseConstValue(lexer);

0 commit comments

Comments
 (0)