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__/__snapshots__/parser.test.ts.snap b/src/__tests__/__snapshots__/parser.test.ts.snap index 1fe3a73..78800df 100644 --- a/src/__tests__/__snapshots__/parser.test.ts.snap +++ b/src/__tests__/__snapshots__/parser.test.ts.snap @@ -644,7 +644,8 @@ exports[`parse > parses the kitchen sink document like graphql.js does 1`] = ` "value": { "block": true, "kind": "StringValue", - "value": "block string uses """", + "value": "block string uses """ +", }, }, ], @@ -768,5 +769,86 @@ exports[`parse > parses the kitchen sink document like graphql.js does 1`] = ` }, ], "kind": "Document", + "loc": { + "end": 1257, + "source": { + "body": "# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery { + whoever123is: node(id: [123, 456]) { + id + ... on User @onInlineFragment { + field2 { + id + alias: field1(first: 10, after: $foo) @include(if: $foo) { + id + ...frag @onFragmentSpread + } + } + } + ... @skip(unless: $foo) { + id + } + ... { + id + } + } +} + +mutation likeStory @onMutation { + like(story: 123) @onField { + story { + id @onField + } + } +} + +subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) +@onSubscription { + storyLikeSubscribe(input: $input) { + story { + likers { + count + } + likeSentence { + text + } + } + } +} + +fragment frag on Friend @onFragmentDefinition { + foo( + size: $site + bar: 12 + obj: { + key: "value" + block: """ + block string uses \\""" + """ + } + ) +} + +query teeny { + unnamed(truthy: true, falsey: false, nullish: null) + query +} + +query tiny { + __typename +} +", + "locationOffset": { + "column": 1, + "line": 1, + }, + "name": "graphql.web", + }, + "start": 0, + }, } `; diff --git a/src/parser.ts b/src/parser.ts index ce339c9..bb18e56 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,35 @@ function document(): ast.DocumentNode { throw error('Document'); } } while (idx < input.length); + + if (!noLoc) { + let loc: Location | undefined; + return { + kind: 'Document' as Kind.DOCUMENT, + definitions, + set loc(_loc: Location) { + loc = _loc; + }, + // @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 +539,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(