Skip to content

Commit f5d48d7

Browse files
committed
Allows buildASTSchema to throw errors with source locations.
1 parent f7b94d1 commit f5d48d7

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

src/error/syntaxError.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import { getLocation } from '../language/location';
1212
import type { Source } from '../language/source';
1313
import { GraphQLError } from './GraphQLError';
14+
import type { ASTNode } from '../language/ast';
1415

1516
/**
1617
* Produces a GraphQLError representing a syntax error, containing useful
@@ -32,6 +33,25 @@ export function syntaxError(
3233
return error;
3334
}
3435

36+
/**
37+
* Produces a string for the invarant(...) function that renders the location
38+
* where an error occurred. If no source is passed in, it renders the message
39+
* without context.
40+
*/
41+
export function invariantError(
42+
message: string,
43+
node: ASTNode,
44+
source: ?Source
45+
): string {
46+
const position = node.loc ? node.loc.start : null;
47+
if (position == null || source == null) {
48+
return message;
49+
}
50+
const location = getLocation(source, position);
51+
return `${message} (${location.line}:${location.column}) ` +
52+
'\n\n' + highlightSourceAtLocation(source, location);
53+
}
54+
3555
/**
3656
* Render a helpful description of the location of the error in the GraphQL
3757
* Source document.

src/utilities/buildASTSchema.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import keyValMap from '../jsutils/keyValMap';
1414
import { valueFromAST } from './valueFromAST';
1515
import { TokenKind } from '../language/lexer';
1616
import { parse } from '../language/parser';
17-
import type { Source } from '../language/source';
17+
import { Source } from '../language/source';
1818
import { getArgumentValues } from '../execution/values';
19+
import { invariantError } from '../error/syntaxError';
1920

2021
import {
2122
LIST_TYPE,
@@ -135,7 +136,10 @@ function getNamedTypeNode(typeNode: TypeNode): NamedTypeNode {
135136
* Given that AST it constructs a GraphQLSchema. The resulting schema
136137
* has no resolve methods, so execution will use default resolvers.
137138
*/
138-
export function buildASTSchema(ast: DocumentNode): GraphQLSchema {
139+
export function buildASTSchema(
140+
ast: DocumentNode,
141+
source?: Source
142+
): GraphQLSchema {
139143
if (!ast || ast.kind !== DOCUMENT) {
140144
throw new Error('Must provide a document ast.');
141145
}
@@ -300,25 +304,29 @@ export function buildASTSchema(ast: DocumentNode): GraphQLSchema {
300304

301305
function produceInputType(typeNode: TypeNode): GraphQLInputType {
302306
const type = produceType(typeNode);
303-
invariant(isInputType(type), 'Expected Input type.');
307+
invariant(isInputType(type),
308+
invariantError('Expected Input type', typeNode, source));
304309
return (type: any);
305310
}
306311

307312
function produceOutputType(typeNode: TypeNode): GraphQLOutputType {
308313
const type = produceType(typeNode);
309-
invariant(isOutputType(type), 'Expected Output type.');
314+
invariant(isOutputType(type),
315+
invariantError('Expected Output type', typeNode, source));
310316
return (type: any);
311317
}
312318

313319
function produceObjectType(typeNode: TypeNode): GraphQLObjectType {
314320
const type = produceType(typeNode);
315-
invariant(type instanceof GraphQLObjectType, 'Expected Object type.');
321+
invariant(type instanceof GraphQLObjectType,
322+
invariantError('Expected Object type', typeNode, source));
316323
return type;
317324
}
318325

319326
function produceInterfaceType(typeNode: TypeNode): GraphQLInterfaceType {
320327
const type = produceType(typeNode);
321-
invariant(type instanceof GraphQLInterfaceType, 'Expected Interface type.');
328+
invariant(type instanceof GraphQLInterfaceType,
329+
invariantError('Expected Interface type', typeNode, source));
322330
return type;
323331
}
324332

@@ -518,7 +526,8 @@ export function getDescription(node: { loc?: Location }): ?string {
518526
* document.
519527
*/
520528
export function buildSchema(source: string | Source): GraphQLSchema {
521-
return buildASTSchema(parse(source));
529+
const sourceObj = typeof source === 'string' ? new Source(source) : source;
530+
return buildASTSchema(parse(sourceObj), sourceObj);
522531
}
523532

524533
// Count the number of spaces on the starting side of a string.

0 commit comments

Comments
 (0)