Skip to content

Commit 02db413

Browse files
committed
Add flag for parser
1 parent 10a3021 commit 02db413

File tree

8 files changed

+69
-18
lines changed

8 files changed

+69
-18
lines changed

src/__testUtils__/kitchenSinkQuery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery {
1010
...frag @onFragmentSpread
1111
}
1212
}
13-
13+
1414
field3!
1515
field4?
1616
requiredField5: field5!

src/execution/__tests__/variables-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ function executeQuery(
160160
query: string,
161161
variableValues?: { [variable: string]: unknown },
162162
) {
163-
const document = parse(query);
163+
const document = parse(query, { experimentalFragmentArguments: true });
164164
return executeSync({ schema, document, variableValues });
165165
}
166166

src/language/__tests__/parser-test.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -607,16 +607,32 @@ describe('Parser', () => {
607607
expect('loc' in result).to.equal(false);
608608
});
609609

610-
it('allows parsing fragment defined arguments', () => {
610+
it('allows parsing fragment defined variables', () => {
611611
const document = 'fragment a($v: Boolean = false) on t { f(v: $v) }';
612612

613-
expect(() => parse(document)).to.not.throw();
613+
expect(() =>
614+
parse(document, { experimentalFragmentArguments: true }),
615+
).to.not.throw();
616+
});
617+
618+
it('disallows parsing fragment defined variables without experimental flag', () => {
619+
const document = 'fragment a($v: Boolean = false) on t { f(v: $v) }';
620+
621+
expect(() => parse(document)).to.throw();
614622
});
615623

616624
it('allows parsing fragment spread arguments', () => {
617625
const document = 'fragment a on t { ...b(v: $v) }';
618626

619-
expect(() => parse(document)).to.not.throw();
627+
expect(() =>
628+
parse(document, { experimentalFragmentArguments: true }),
629+
).to.not.throw();
630+
});
631+
632+
it('disallows parsing fragment spread arguments without experimental flag', () => {
633+
const document = 'fragment a on t { ...b(v: $v) }';
634+
635+
expect(() => parse(document)).to.throw();
620636
});
621637

622638
it('contains location that can be Object.toStringified, JSON.stringified, or jsutils.inspected', () => {

src/language/__tests__/printer-test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ describe('Printer: Query document', () => {
113113
it('prints fragment with argument definition directives', () => {
114114
const fragmentWithArgumentDefinitionDirective = parse(
115115
'fragment Foo($foo: TestType @test) on TestType @testDirective { id }',
116+
{ experimentalFragmentArguments: true },
116117
);
117118
expect(print(fragmentWithArgumentDefinitionDirective)).to.equal(dedent`
118119
fragment Foo($foo: TestType @test) on TestType @testDirective {
@@ -128,6 +129,7 @@ describe('Printer: Query document', () => {
128129
id
129130
}
130131
`,
132+
{ experimentalFragmentArguments: true },
131133
);
132134
expect(print(fragmentWithArgumentDefinition)).to.equal(dedent`
133135
fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
@@ -139,6 +141,7 @@ describe('Printer: Query document', () => {
139141
it('prints fragment spread with arguments', () => {
140142
const fragmentSpreadWithArguments = parse(
141143
'fragment Foo on TestType { ...Bar(a: {x: $x}, b: true) }',
144+
{ experimentalFragmentArguments: true },
142145
);
143146
expect(print(fragmentSpreadWithArguments)).to.equal(dedent`
144147
fragment Foo on TestType {
@@ -150,6 +153,7 @@ describe('Printer: Query document', () => {
150153
it('prints fragment spread with multi-line arguments', () => {
151154
const fragmentSpreadWithArguments = parse(
152155
'fragment Foo on TestType { ...Bar(a: {x: $x, y: $y, z: $z, xy: $xy}, b: true, c: "a long string extending arguments over max length") }',
156+
{ experimentalFragmentArguments: true },
153157
);
154158
expect(print(fragmentSpreadWithArguments)).to.equal(dedent`
155159
fragment Foo on TestType {

src/language/__tests__/visitor-test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ describe('Visitor', () => {
458458
it('visits arguments defined on fragments', () => {
459459
const ast = parse('fragment a($v: Boolean = false) on t { f }', {
460460
noLocation: true,
461+
experimentalFragmentArguments: true,
461462
});
462463
const visited: Array<any> = [];
463464

@@ -507,6 +508,7 @@ describe('Visitor', () => {
507508
it('visits arguments on fragment spreads', () => {
508509
const ast = parse('fragment a on t { ...s(v: false) }', {
509510
noLocation: true,
511+
experimentalFragmentArguments: true,
510512
});
511513
const visited: Array<any> = [];
512514

src/language/parser.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,27 @@ export interface ParseOptions {
9191
*/
9292
maxTokens?: number | undefined;
9393

94+
/**
95+
* EXPERIMENTAL:
96+
*
97+
* If enabled, the parser will understand and parse fragment variable definitions
98+
* and arguments on fragment spreads. Fragment variable definitions will be represented
99+
* in the `variableDefinitions` field of the FragmentDefinitionNode.
100+
* Fragment spread arguments will be represented in the `arguments` field of FragmentSpreadNode.
101+
*
102+
* For example:
103+
*
104+
* ```graphql
105+
* {
106+
* t { ...A(var: true) }
107+
* }
108+
* fragment A($var: Boolean = false) on T {
109+
* ...B(x: $var)
110+
* }
111+
* ```
112+
*/
113+
experimentalFragmentArguments?: boolean | undefined;
114+
94115
/**
95116
* EXPERIMENTAL:
96117
*
@@ -544,7 +565,10 @@ export class Parser {
544565
const hasTypeCondition = this.expectOptionalKeyword('on');
545566
if (!hasTypeCondition && this.peek(TokenKind.NAME)) {
546567
const name = this.parseFragmentName();
547-
if (this.peek(TokenKind.PAREN_L)) {
568+
if (
569+
this.peek(TokenKind.PAREN_L) &&
570+
this._options.experimentalFragmentArguments
571+
) {
548572
return this.node<FragmentSpreadNode>(start, {
549573
kind: Kind.FRAGMENT_SPREAD,
550574
name,
@@ -578,7 +602,9 @@ export class Parser {
578602
return this.node<FragmentDefinitionNode>(start, {
579603
kind: Kind.FRAGMENT_DEFINITION,
580604
name: this.parseFragmentName(),
581-
variableDefinitions: this.parseVariableDefinitions(),
605+
variableDefinitions: this._options.experimentalFragmentArguments
606+
? this.parseVariableDefinitions()
607+
: undefined,
582608
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
583609
directives: this.parseDirectives(false),
584610
selectionSet: this.parseSelectionSet(),

src/utilities/__tests__/TypeInfo-test.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -519,17 +519,20 @@ describe('visitWithTypeInfo', () => {
519519
it('supports traversals of fragment arguments', () => {
520520
const typeInfo = new TypeInfo(testSchema);
521521

522-
const ast = parse(`
523-
query {
524-
...Foo(x: 4)
525-
}
522+
const ast = parse(
523+
`
524+
query {
525+
...Foo(x: 4)
526+
}
526527
527-
fragment Foo(
528-
$x: ID!
529-
) on QueryRoot {
530-
human(id: $x) { name }
531-
}
532-
`);
528+
fragment Foo(
529+
$x: ID!
530+
) on QueryRoot {
531+
human(id: $x) { name }
532+
}
533+
`,
534+
{ experimentalFragmentArguments: true },
535+
);
533536

534537
const visited: Array<any> = [];
535538
visit(

src/validation/__tests__/harness.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export function expectValidationErrorsWithSchema(
123123
rule: ValidationRule,
124124
queryStr: string,
125125
): any {
126-
const doc = parse(queryStr);
126+
const doc = parse(queryStr, { experimentalFragmentArguments: true });
127127
const errors = validate(schema, doc, [rule]);
128128
return expectJSON(errors);
129129
}

0 commit comments

Comments
 (0)