diff --git a/src/language/__tests__/parser-test.js b/src/language/__tests__/parser-test.js index cdab9972bd..6271792e82 100644 --- a/src/language/__tests__/parser-test.js +++ b/src/language/__tests__/parser-test.js @@ -21,16 +21,6 @@ function expectSyntaxError(text: string) { } describe('Parser', () => { - it('asserts that a source to parse was provided', () => { - // $FlowExpectedError[incompatible-call] - expect(() => parse()).to.throw('Must provide Source. Received: undefined.'); - }); - - it('asserts that an invalid source to parse was provided', () => { - // $FlowExpectedError[incompatible-call] - expect(() => parse({})).to.throw('Must provide Source. Received: {}.'); - }); - it('parse provides useful errors', () => { let caughtError; try { diff --git a/src/language/__tests__/source-test.js b/src/language/__tests__/source-test.js index 35b72e4985..31a34aa16d 100644 --- a/src/language/__tests__/source-test.js +++ b/src/language/__tests__/source-test.js @@ -4,6 +4,20 @@ import { describe, it } from 'mocha'; import { Source } from '../source'; describe('Source', () => { + it('asserts that a body was provided', () => { + // $FlowExpectedError[incompatible-call] + expect(() => new Source()).to.throw( + 'Body must be a string. Received: undefined.', + ); + }); + + it('asserts that a valid body was provided', () => { + // $FlowExpectedError[incompatible-call] + expect(() => new Source({})).to.throw( + 'Body must be a string. Received: {}.', + ); + }); + it('can be Object.toStringified', () => { const source = new Source(''); diff --git a/src/language/parser.js b/src/language/parser.js index 5fb83ddbb4..5d7b16186d 100644 --- a/src/language/parser.js +++ b/src/language/parser.js @@ -1,6 +1,3 @@ -import inspect from '../jsutils/inspect'; -import devAssert from '../jsutils/devAssert'; - import type { GraphQLError } from '../error/GraphQLError'; import { syntaxError } from '../error/syntaxError'; @@ -53,8 +50,8 @@ import type { } from './ast'; import { Kind } from './kinds'; import { Location } from './ast'; -import { Source } from './source'; import { TokenKind } from './tokenKind'; +import { Source, isSource } from './source'; import { DirectiveLocation } from './directiveLocation'; import { Lexer, isPunctuatorTokenKind } from './lexer'; @@ -178,11 +175,7 @@ export class Parser { _lexer: Lexer; constructor(source: string | Source, options?: ParseOptions) { - const sourceObj = typeof source === 'string' ? new Source(source) : source; - devAssert( - sourceObj instanceof Source, - `Must provide Source. Received: ${inspect(sourceObj)}.`, - ); + const sourceObj = isSource(source) ? source : new Source(source); this._lexer = new Lexer(sourceObj); this._options = options; diff --git a/src/language/source.d.ts b/src/language/source.d.ts index 433bf00e5d..a7df7cbb6b 100644 --- a/src/language/source.d.ts +++ b/src/language/source.d.ts @@ -16,3 +16,10 @@ export class Source { locationOffset: Location; constructor(body: string, name?: string, locationOffset?: Location); } + +/** + * Test if the given value is a Source object. + * + * @internal + */ +export function isSource(source: any): source is Source; diff --git a/src/language/source.js b/src/language/source.js index 74a823d6fa..05a0fc9c81 100644 --- a/src/language/source.js +++ b/src/language/source.js @@ -1,6 +1,8 @@ import { SYMBOL_TO_STRING_TAG } from '../polyfills/symbols'; +import inspect from '../jsutils/inspect'; import devAssert from '../jsutils/devAssert'; +import instanceOf from '../jsutils/instanceOf'; type Location = {| line: number, @@ -24,6 +26,11 @@ export class Source { name: string = 'GraphQL request', locationOffset: Location = { line: 1, column: 1 }, ): void { + devAssert( + typeof body === 'string', + `Body must be a string. Received: ${inspect(body)}.`, + ); + this.body = body; this.name = name; this.locationOffset = locationOffset; @@ -42,3 +49,15 @@ export class Source { return 'Source'; } } + +/** + * Test if the given value is a Source object. + * + * @internal + */ +declare function isSource(source: mixed): boolean %checks(source instanceof + Source); +// eslint-disable-next-line no-redeclare +export function isSource(source) { + return instanceOf(source, Source); +} diff --git a/src/utilities/__tests__/stripIgnoredCharacters-test.js b/src/utilities/__tests__/stripIgnoredCharacters-test.js index 335d193a5a..87ebd5b5e2 100644 --- a/src/utilities/__tests__/stripIgnoredCharacters-test.js +++ b/src/utilities/__tests__/stripIgnoredCharacters-test.js @@ -6,9 +6,9 @@ import inspectStr from '../../__testUtils__/inspectStr'; import invariant from '../../jsutils/invariant'; +import { Lexer } from '../../language/lexer'; import { parse } from '../../language/parser'; import { Source } from '../../language/source'; -import { Lexer } from '../../language/lexer'; import { stripIgnoredCharacters } from '../stripIgnoredCharacters'; @@ -98,20 +98,6 @@ function expectStripped(docString: string) { } describe('stripIgnoredCharacters', () => { - it('asserts that a source was provided', () => { - // $FlowExpectedError[incompatible-call] - expect(() => stripIgnoredCharacters()).to.throw( - 'Must provide string or Source. Received: undefined.', - ); - }); - - it('asserts that a valid source was provided', () => { - // $FlowExpectedError[incompatible-call] - expect(() => stripIgnoredCharacters({})).to.throw( - 'Must provide string or Source. Received: {}.', - ); - }); - it('strips ignored characters from GraphQL query document', () => { const query = dedent` query SomeQuery($foo: String!, $bar: String) { @@ -130,6 +116,10 @@ describe('stripIgnoredCharacters', () => { ); }); + it('accepts Source object', () => { + expect(stripIgnoredCharacters(new Source('{ a }'))).to.equal('{a}'); + }); + it('strips ignored characters from GraphQL SDL document', () => { const sdl = dedent` """ diff --git a/src/utilities/stripIgnoredCharacters.js b/src/utilities/stripIgnoredCharacters.js index 15505a4c8e..f4f53aa042 100644 --- a/src/utilities/stripIgnoredCharacters.js +++ b/src/utilities/stripIgnoredCharacters.js @@ -1,6 +1,4 @@ -import inspect from '../jsutils/inspect'; - -import { Source } from '../language/source'; +import { Source, isSource } from '../language/source'; import { TokenKind } from '../language/tokenKind'; import { Lexer, isPunctuatorTokenKind } from '../language/lexer'; import { @@ -61,12 +59,7 @@ import { * """Type description""" type Foo{"""Field description""" bar:String} */ export function stripIgnoredCharacters(source: string | Source): string { - const sourceObj = typeof source === 'string' ? new Source(source) : source; - if (!(sourceObj instanceof Source)) { - throw new TypeError( - `Must provide string or Source. Received: ${inspect(sourceObj)}.`, - ); - } + const sourceObj = isSource(source) ? source : new Source(source); const body = sourceObj.body; const lexer = new Lexer(sourceObj);