diff --git a/.changeset/tiny-gifts-exercise.md b/.changeset/tiny-gifts-exercise.md new file mode 100644 index 0000000..587eb4b --- /dev/null +++ b/.changeset/tiny-gifts-exercise.md @@ -0,0 +1,5 @@ +--- +'@0no-co/graphql.web': patch +--- + +Add `loc` getter to parsed `DocumentNode` fragment outputs to ensure that using fragments created by `gql.tada`'s `graphql()` function with `graphql-tag` doesn't crash. `graphql-tag` does not treat the `DocumentNode.loc` property as optional on interpolations, which leads to intercompatibility issues. diff --git a/src/__tests__/parser.test.ts b/src/__tests__/parser.test.ts index 8e5d1be..88e01a9 100644 --- a/src/__tests__/parser.test.ts +++ b/src/__tests__/parser.test.ts @@ -6,7 +6,7 @@ import { Kind } from '../kind'; describe('parse', () => { it('parses the kitchen sink document like graphql.js does', () => { - const doc = parse(kitchenSinkDocument); + const doc = parse(kitchenSinkDocument, { noLocation: true }); expect(doc).toMatchSnapshot(); }); diff --git a/src/parser.ts b/src/parser.ts index ce339c9..7754df8 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -6,7 +6,7 @@ */ import type { Kind, OperationTypeNode } from './kind'; import { GraphQLError } from './error'; -import type { Source } from './types'; +import type { Location, Source } from './types'; import type * as ast from './ast'; let input: string; @@ -483,7 +483,7 @@ function operationDefinition( } } -function document(): ast.DocumentNode { +function document(input: string, noLoc: boolean): ast.DocumentNode { let match: string | undefined; let definition: ast.OperationDefinitionNode | undefined; ignored(); @@ -498,6 +498,37 @@ function document(): ast.DocumentNode { throw error('Document'); } } while (idx < input.length); + + if (!noLoc) { + let loc: Location | undefined; + return { + kind: 'Document' as Kind.DOCUMENT, + definitions, + /* v8 ignore start */ + set loc(_loc: Location) { + loc = _loc; + }, + /* v8 ignore stop */ + // @ts-ignore + get loc() { + if (!loc) { + loc = { + start: 0, + end: input.length, + startToken: undefined, + endToken: undefined, + source: { + body: input, + name: 'graphql.web', + locationOffset: { line: 1, column: 1 }, + }, + }; + } + return loc; + }, + }; + } + return { kind: 'Document' as Kind.DOCUMENT, definitions, @@ -510,11 +541,11 @@ type ParseOptions = { export function parse( string: string | Source, - _options?: ParseOptions | undefined + options?: ParseOptions | undefined ): ast.DocumentNode { input = typeof string.body === 'string' ? string.body : string; idx = 0; - return document(); + return document(input, options && options.noLocation); } export function parseValue(