Skip to content

Commit cb52a99

Browse files
committed
add support for fragment arguments
1 parent 3f3b440 commit cb52a99

File tree

6 files changed

+188
-1
lines changed

6 files changed

+188
-1
lines changed

src/__tests__/__snapshots__/parser.test.ts.snap

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ exports[`parse > parses the kitchen sink document like graphql.js does 1`] = `
185185
"selectionSet": undefined,
186186
},
187187
{
188+
"arguments": [],
188189
"directives": [
189190
{
190191
"arguments": [],
@@ -644,7 +645,8 @@ exports[`parse > parses the kitchen sink document like graphql.js does 1`] = `
644645
"value": {
645646
"block": true,
646647
"kind": "StringValue",
647-
"value": "block string uses """",
648+
"value": "block string uses """
649+
",
648650
},
649651
},
650652
],
@@ -669,6 +671,7 @@ exports[`parse > parses the kitchen sink document like graphql.js does 1`] = `
669671
"value": "Friend",
670672
},
671673
},
674+
"variableDefinitions": undefined,
672675
},
673676
{
674677
"directives": [],

src/__tests__/parser.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,90 @@ describe('parse', () => {
106106
expect(() => parse('fragment Name on Type { field }')).not.toThrow();
107107
});
108108

109+
it('parses fragment variable-definitions', () => {
110+
expect(parse('fragment x on Type ($var: Int = 1) { field }').definitions[0]).toEqual({
111+
kind: Kind.FRAGMENT_DEFINITION,
112+
directives: [],
113+
name: {
114+
kind: Kind.NAME,
115+
value: 'x',
116+
},
117+
typeCondition: {
118+
kind: Kind.NAMED_TYPE,
119+
name: {
120+
kind: Kind.NAME,
121+
value: 'Type',
122+
},
123+
},
124+
variableDefinitions: [
125+
{
126+
kind: Kind.VARIABLE_DEFINITION,
127+
type: {
128+
kind: Kind.NAMED_TYPE,
129+
name: {
130+
kind: Kind.NAME,
131+
value: 'Int',
132+
},
133+
},
134+
variable: {
135+
kind: Kind.VARIABLE,
136+
name: {
137+
kind: Kind.NAME,
138+
value: 'var',
139+
},
140+
},
141+
defaultValue: {
142+
kind: Kind.INT,
143+
value: '1',
144+
},
145+
directives: [],
146+
},
147+
],
148+
selectionSet: {
149+
kind: Kind.SELECTION_SET,
150+
selections: [
151+
{
152+
alias: undefined,
153+
kind: Kind.FIELD,
154+
directives: [],
155+
selectionSet: undefined,
156+
arguments: [],
157+
name: {
158+
kind: Kind.NAME,
159+
value: 'field',
160+
},
161+
},
162+
],
163+
},
164+
});
165+
});
166+
167+
it.only('parses fragment-spread arguments', () => {
168+
expect(
169+
parse('query x { ...x(var: 2) } fragment x on Type ($var: Int = 1) { field }').definitions[0]
170+
).toHaveProperty('selectionSet.selections.0', {
171+
kind: Kind.FRAGMENT_SPREAD,
172+
directives: [],
173+
name: {
174+
kind: Kind.NAME,
175+
value: 'x',
176+
},
177+
arguments: [
178+
{
179+
kind: 'Argument',
180+
name: {
181+
kind: 'Name',
182+
value: 'var',
183+
},
184+
value: {
185+
kind: 'IntValue',
186+
value: '2',
187+
},
188+
},
189+
],
190+
});
191+
});
192+
109193
it('parses fields', () => {
110194
expect(() => parse('{ field: }')).toThrow();
111195
expect(() => parse('{ alias: field() }')).toThrow();

src/__tests__/printer.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as graphql16 from 'graphql16';
44
import { parse } from '../parser';
55
import { print, printString, printBlockString } from '../printer';
66
import kitchenSinkAST from './fixtures/kitchen_sink.json';
7+
import { Kind } from 'src/kind';
78

89
function dedentString(string: string) {
910
const trimmedStr = string
@@ -115,6 +116,94 @@ describe('print', () => {
115116
).toBe('[Type!]');
116117
});
117118

119+
it('prints fragment-definition with variables', () => {
120+
expect(
121+
print({
122+
kind: Kind.FRAGMENT_DEFINITION,
123+
directives: [],
124+
name: {
125+
kind: Kind.NAME,
126+
value: 'x',
127+
},
128+
typeCondition: {
129+
kind: Kind.NAMED_TYPE,
130+
name: {
131+
kind: Kind.NAME,
132+
value: 'Type',
133+
},
134+
},
135+
variableDefinitions: [
136+
{
137+
kind: Kind.VARIABLE_DEFINITION,
138+
type: {
139+
kind: Kind.NAMED_TYPE,
140+
name: {
141+
kind: Kind.NAME,
142+
value: 'Int',
143+
},
144+
},
145+
variable: {
146+
kind: Kind.VARIABLE,
147+
name: {
148+
kind: Kind.NAME,
149+
value: 'var',
150+
},
151+
},
152+
defaultValue: {
153+
kind: Kind.INT,
154+
value: '1',
155+
},
156+
directives: [],
157+
},
158+
],
159+
selectionSet: {
160+
kind: Kind.SELECTION_SET,
161+
selections: [
162+
{
163+
alias: undefined,
164+
kind: Kind.FIELD,
165+
directives: [],
166+
selectionSet: undefined,
167+
arguments: [],
168+
name: {
169+
kind: Kind.NAME,
170+
value: 'field',
171+
},
172+
},
173+
],
174+
},
175+
} as any)
176+
).toBe(`fragment x on Type($var: Int = 1) {
177+
field
178+
}`);
179+
});
180+
181+
it('prints fragment-spread with arguments', () => {
182+
expect(
183+
print({
184+
kind: Kind.FRAGMENT_SPREAD,
185+
directives: [],
186+
name: {
187+
kind: Kind.NAME,
188+
value: 'x',
189+
},
190+
arguments: [
191+
{
192+
kind: 'Argument',
193+
name: {
194+
kind: 'Name',
195+
value: 'var',
196+
},
197+
value: {
198+
kind: 'IntValue',
199+
value: '2',
200+
},
201+
},
202+
],
203+
} as any)
204+
).toBe(`...x(var: 2)`);
205+
});
206+
118207
// NOTE: The shim won't throw for invalid AST nodes
119208
it('returns empty strings for invalid AST', () => {
120209
const badAST = { random: 'Data' };

src/ast.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ export type FragmentSpreadNode = Or<
188188
{
189189
readonly kind: Kind.FRAGMENT_SPREAD;
190190
readonly name: NameNode;
191+
readonly arguments?: ReadonlyArray<ArgumentNode>;
191192
readonly directives?: ReadonlyArray<DirectiveNode>;
192193
readonly loc?: Location;
193194
}
@@ -209,6 +210,7 @@ export type FragmentDefinitionNode = Or<
209210
{
210211
readonly kind: Kind.FRAGMENT_DEFINITION;
211212
readonly name: NameNode;
213+
readonly variableDefinitions?: ReadonlyArray<VariableDefinitionNode>;
212214
readonly typeCondition: NamedTypeNode;
213215
readonly directives?: ReadonlyArray<DirectiveNode>;
214216
readonly selectionSet: SelectionSetNode;

src/parser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,12 @@ function fragmentSpread(): ast.FragmentSpreadNode | ast.InlineFragmentNode | und
319319
const _idx = idx;
320320
let _name: ast.NameNode | undefined;
321321
if ((_name = name()) && _name.value !== 'on') {
322+
const _arguments = arguments_(false);
322323
return {
323324
kind: 'FragmentSpread' as Kind.FRAGMENT_SPREAD,
324325
name: _name,
325326
directives: directives(false),
327+
arguments: _arguments,
326328
};
327329
} else {
328330
idx = _idx;
@@ -404,14 +406,17 @@ function fragmentDefinition(): ast.FragmentDefinitionNode | undefined {
404406
if (!_name) throw error('FragmentDefinition');
405407
ignored();
406408
const _typeCondition = typeCondition();
409+
407410
if (!_typeCondition) throw error('FragmentDefinition');
411+
const _variableDefinitions = variableDefinitions();
408412
const _directives = directives(false);
409413
const _selectionSet = selectionSet();
410414
if (!_selectionSet) throw error('FragmentDefinition');
411415
return {
412416
kind: 'FragmentDefinition' as Kind.FRAGMENT_DEFINITION,
413417
name: _name,
414418
typeCondition: _typeCondition,
419+
variableDefinitions: _variableDefinitions.length ? _variableDefinitions : undefined,
415420
directives: _directives,
416421
selectionSet: _selectionSet,
417422
};

src/printer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ const nodes: {
9797
},
9898
FragmentSpread(node) {
9999
let out = '...' + node.name.value;
100+
if (hasItems(node.arguments)) out += '(' + node.arguments.map(nodes.Argument!).join(', ') + ')';
100101
if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
101102
return out;
102103
},
@@ -109,6 +110,9 @@ const nodes: {
109110
FragmentDefinition(node) {
110111
let out = 'fragment ' + node.name.value;
111112
out += ' on ' + node.typeCondition.name.value;
113+
if (hasItems(node.variableDefinitions)) {
114+
out += '(' + node.variableDefinitions.map(nodes.VariableDefinition!).join(', ') + ')';
115+
}
112116
if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
113117
return out + ' ' + print(node.selectionSet);
114118
},

0 commit comments

Comments
 (0)