Skip to content

Commit 1d7efa9

Browse files
authored
[RFC] Allow directives on variable definitions (#1437)
* [RFC] Allow directives on variable definitions * Make parser expect const directives, and move to match position with InputValueDefinition * Adding to introspection query, and the KnownDirectives validation rule
1 parent 3217802 commit 1d7efa9

File tree

13 files changed

+53
-10
lines changed

13 files changed

+53
-10
lines changed

src/language/__tests__/parser-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ describe('Parser', () => {
106106
);
107107
});
108108

109+
it('parses variable definition directives', () => {
110+
expect(() =>
111+
parse('query Foo($x: Boolean = false @bar) { field }'),
112+
).to.not.throw();
113+
});
114+
109115
it('does not accept fragments named "on"', () => {
110116
expectSyntaxError('fragment on on on { on }', 'Unexpected Name "on"', {
111117
line: 1,

src/language/__tests__/printer-test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ describe('Printer: Query document', () => {
6464
}
6565
`);
6666

67+
const queryAstWithVariableDirective = parse(
68+
'query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { id }',
69+
);
70+
expect(print(queryAstWithVariableDirective)).to.equal(dedent`
71+
query ($foo: TestType = {a: 123} @testDirective(if: true) @test) {
72+
id
73+
}
74+
`);
75+
6776
const mutationAstWithArtifacts = parse(
6877
'mutation ($foo: TestType) @testDirective { id, name }',
6978
);

src/language/ast.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ export type VariableDefinitionNode = {
225225
+variable: VariableNode,
226226
+type: TypeNode,
227227
+defaultValue?: ValueNode,
228+
+directives?: $ReadOnlyArray<DirectiveNode>,
228229
};
229230

230231
export type VariableNode = {

src/language/directiveLocation.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const DirectiveLocation = Object.freeze({
1919
FRAGMENT_DEFINITION: 'FRAGMENT_DEFINITION',
2020
FRAGMENT_SPREAD: 'FRAGMENT_SPREAD',
2121
INLINE_FRAGMENT: 'INLINE_FRAGMENT',
22+
VARIABLE_DEFINITION: 'VARIABLE_DEFINITION',
2223
// Type System Definitions
2324
SCHEMA: 'SCHEMA',
2425
SCALAR: 'SCALAR',

src/language/parser.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ function parseVariableDefinitions(
332332
}
333333

334334
/**
335-
* VariableDefinition : Variable : Type DefaultValue?
335+
* VariableDefinition : Variable : Type DefaultValue? Directives[Const]?
336336
*/
337337
function parseVariableDefinition(lexer: Lexer<*>): VariableDefinitionNode {
338338
const start = lexer.token;
@@ -343,6 +343,7 @@ function parseVariableDefinition(lexer: Lexer<*>): VariableDefinitionNode {
343343
defaultValue: skip(lexer, TokenKind.EQUALS)
344344
? parseValueLiteral(lexer, true)
345345
: undefined,
346+
directives: parseDirectives(lexer, true),
346347
loc: loc(lexer, start),
347348
};
348349
}

src/language/printer.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ const printDocASTReducer = {
3636
: join([op, join([name, varDefs]), directives, selectionSet], ' ');
3737
},
3838

39-
VariableDefinition: ({ variable, type, defaultValue }) =>
40-
variable + ': ' + type + wrap(' = ', defaultValue),
41-
39+
VariableDefinition: ({ variable, type, defaultValue, directives }) =>
40+
variable +
41+
': ' +
42+
type +
43+
wrap(' = ', defaultValue) +
44+
wrap(' ', join(directives, ' ')),
4245
SelectionSet: ({ selections }) => block(selections),
4346

4447
Field: ({ alias, name, arguments: args, directives, selectionSet }) =>

src/language/visitor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export const QueryDocumentKeys = {
6464
'directives',
6565
'selectionSet',
6666
],
67-
VariableDefinition: ['variable', 'type', 'defaultValue'],
67+
VariableDefinition: ['variable', 'type', 'defaultValue', 'directives'],
6868
Variable: ['name'],
6969
SelectionSet: ['selections'],
7070
Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'],

src/type/__tests__/introspection-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,11 @@ describe('Introspection', () => {
746746
isDeprecated: false,
747747
deprecationReason: null,
748748
},
749+
{
750+
name: 'VARIABLE_DEFINITION',
751+
isDeprecated: false,
752+
deprecationReason: null,
753+
},
749754
{
750755
name: 'SCHEMA',
751756
isDeprecated: false,

src/type/introspection.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ export const __DirectiveLocation = new GraphQLEnumType({
135135
value: DirectiveLocation.INLINE_FRAGMENT,
136136
description: 'Location adjacent to an inline fragment.',
137137
},
138+
VARIABLE_DEFINITION: {
139+
value: DirectiveLocation.VARIABLE_DEFINITION,
140+
description: 'Location adjacent to a variable definition.',
141+
},
138142
SCHEMA: {
139143
value: DirectiveLocation.SCHEMA,
140144
description: 'Location adjacent to a schema definition.',

src/utilities/__tests__/schemaPrinter-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,9 @@ describe('Type System Printer', () => {
676676
"""Location adjacent to an inline fragment."""
677677
INLINE_FRAGMENT
678678
679+
"""Location adjacent to a variable definition."""
680+
VARIABLE_DEFINITION
681+
679682
"""Location adjacent to a schema definition."""
680683
SCHEMA
681684
@@ -904,6 +907,9 @@ describe('Type System Printer', () => {
904907
# Location adjacent to an inline fragment.
905908
INLINE_FRAGMENT
906909
910+
# Location adjacent to a variable definition.
911+
VARIABLE_DEFINITION
912+
907913
# Location adjacent to a schema definition.
908914
SCHEMA
909915

src/validation/__tests__/KnownDirectives-test.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ describe('Validate: Known directives', () => {
127127
expectPassesRule(
128128
KnownDirectives,
129129
`
130-
query Foo @onQuery {
131-
name @include(if: true)
130+
query Foo($var: Boolean @onVariableDefinition) @onQuery {
131+
name @include(if: $var)
132132
...Frag @include(if: true)
133133
skippedField @skip(if: true)
134134
...SkippedFrag @skip(if: true)
@@ -145,8 +145,8 @@ describe('Validate: Known directives', () => {
145145
expectFailsRule(
146146
KnownDirectives,
147147
`
148-
query Foo @include(if: true) {
149-
name @onQuery
148+
query Foo($var: Boolean @onField) @include(if: true) {
149+
name @onQuery @include(if: $var)
150150
...Frag @onQuery
151151
}
152152
@@ -155,7 +155,8 @@ describe('Validate: Known directives', () => {
155155
}
156156
`,
157157
[
158-
misplacedDirective('include', 'QUERY', 2, 17),
158+
misplacedDirective('onField', 'VARIABLE_DEFINITION', 2, 31),
159+
misplacedDirective('include', 'QUERY', 2, 41),
159160
misplacedDirective('onQuery', 'FIELD', 3, 14),
160161
misplacedDirective('onQuery', 'FRAGMENT_SPREAD', 4, 17),
161162
misplacedDirective('onQuery', 'MUTATION', 7, 20),

src/validation/__tests__/harness.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ export const testSchema = new GraphQLSchema({
374374
name: 'onInlineFragment',
375375
locations: ['INLINE_FRAGMENT'],
376376
}),
377+
new GraphQLDirective({
378+
name: 'onVariableDefinition',
379+
locations: ['VARIABLE_DEFINITION'],
380+
}),
377381
],
378382
});
379383

src/validation/rules/KnownDirectives.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ function getDirectiveLocationForASTPath(ancestors) {
9898
return DirectiveLocation.INLINE_FRAGMENT;
9999
case Kind.FRAGMENT_DEFINITION:
100100
return DirectiveLocation.FRAGMENT_DEFINITION;
101+
case Kind.VARIABLE_DEFINITION:
102+
return DirectiveLocation.VARIABLE_DEFINITION;
101103
case Kind.SCHEMA_DEFINITION:
102104
case Kind.SCHEMA_EXTENSION:
103105
return DirectiveLocation.SCHEMA;

0 commit comments

Comments
 (0)