Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/__testUtils__/kitchenSinkSDL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,8 @@ extend schema @onSchema
extend schema @onSchema {
subscription: SubscriptionType
}

directive @myDirective @onDirective on OBJECT | FIELD_DEFINITION

extend directive @myDirective @onDirective2
`;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ export type {
UnionTypeExtensionNode,
EnumTypeExtensionNode,
InputObjectTypeExtensionNode,
DirectiveExtensionNode,
// Schema Coordinates
SchemaCoordinateNode,
TypeCoordinateNode,
Expand Down
2 changes: 2 additions & 0 deletions src/language/__tests__/predicates-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('AST node predicates', () => {
'InputObjectTypeDefinition',
'DirectiveDefinition',
'SchemaExtension',
'DirectiveExtension',
'ScalarTypeExtension',
'ObjectTypeExtension',
'InterfaceTypeExtension',
Expand Down Expand Up @@ -123,6 +124,7 @@ describe('AST node predicates', () => {
it('isTypeSystemExtensionNode', () => {
expect(filterNodes(isTypeSystemExtensionNode)).to.deep.equal([
'SchemaExtension',
'DirectiveExtension',
'ScalarTypeExtension',
'ObjectTypeExtension',
'InterfaceTypeExtension',
Expand Down
2 changes: 2 additions & 0 deletions src/language/__tests__/schema-parser-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,7 @@ input Hello {
{
kind: 'DirectiveDefinition',
description: undefined,
directives: [],
name: {
kind: 'Name',
value: 'foo',
Expand Down Expand Up @@ -1065,6 +1066,7 @@ input Hello {
{
kind: 'DirectiveDefinition',
description: undefined,
directives: [],
name: {
kind: 'Name',
value: 'foo',
Expand Down
4 changes: 4 additions & 0 deletions src/language/__tests__/schema-printer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ describe('Printer: SDL document', () => {
extend schema @onSchema {
subscription: SubscriptionType
}

directive @myDirective @onDirective on OBJECT | FIELD_DEFINITION

extend directive @myDirective @onDirective2
`);
});
});
24 changes: 22 additions & 2 deletions src/language/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export type ASTNode =
| UnionTypeExtensionNode
| EnumTypeExtensionNode
| InputObjectTypeExtensionNode
| DirectiveExtensionNode
| TypeCoordinateNode
| MemberCoordinateNode
| ArgumentCoordinateNode
Expand Down Expand Up @@ -280,10 +281,18 @@ export const QueryDocumentKeys: {
EnumValueDefinition: ['description', 'name', 'directives'],
InputObjectTypeDefinition: ['description', 'name', 'directives', 'fields'],

DirectiveDefinition: ['description', 'name', 'arguments', 'locations'],
DirectiveDefinition: [
'description',
'name',
'arguments',
'directives',
'locations',
],

SchemaExtension: ['directives', 'operationTypes'],

DirectiveExtension: ['name', 'directives'],

ScalarTypeExtension: ['name', 'directives'],
ObjectTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
InterfaceTypeExtension: ['name', 'interfaces', 'directives', 'fields'],
Expand Down Expand Up @@ -686,13 +695,17 @@ export interface DirectiveDefinitionNode {
readonly description?: StringValueNode;
readonly name: NameNode;
readonly arguments?: ReadonlyArray<InputValueDefinitionNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly repeatable: boolean;
readonly locations: ReadonlyArray<NameNode>;
}

/** Type System Extensions */

export type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode;
export type TypeSystemExtensionNode =
| SchemaExtensionNode
| TypeExtensionNode
| DirectiveExtensionNode;

export interface SchemaExtensionNode {
readonly kind: Kind.SCHEMA_EXTENSION;
Expand Down Expand Up @@ -760,6 +773,13 @@ export interface InputObjectTypeExtensionNode {
readonly fields?: ReadonlyArray<InputValueDefinitionNode>;
}

export interface DirectiveExtensionNode {
readonly kind: Kind.DIRECTIVE_EXTENSION;
readonly loc?: Location;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
}

/** Schema Coordinates */

export type SchemaCoordinateNode =
Expand Down
1 change: 1 addition & 0 deletions src/language/directiveLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum DirectiveLocation {
ENUM_VALUE = 'ENUM_VALUE',
INPUT_OBJECT = 'INPUT_OBJECT',
INPUT_FIELD_DEFINITION = 'INPUT_FIELD_DEFINITION',
DIRECTIVE_DEFINITION = 'DIRECTIVE_DEFINITION',
}
export { DirectiveLocation };

Expand Down
1 change: 1 addition & 0 deletions src/language/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export type {
UnionTypeExtensionNode,
EnumTypeExtensionNode,
InputObjectTypeExtensionNode,
DirectiveExtensionNode,
// Schema Coordinates
SchemaCoordinateNode,
TypeCoordinateNode,
Expand Down
1 change: 1 addition & 0 deletions src/language/kinds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ enum Kind {

/** Type System Extensions */
SCHEMA_EXTENSION = 'SchemaExtension',
DIRECTIVE_EXTENSION = 'DirectiveExtension',

/** Type Extensions */
SCALAR_TYPE_EXTENSION = 'ScalarTypeExtension',
Expand Down
24 changes: 24 additions & 0 deletions src/language/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
DirectiveArgumentCoordinateNode,
DirectiveCoordinateNode,
DirectiveDefinitionNode,
DirectiveExtensionNode,
DirectiveNode,
DocumentNode,
EnumTypeDefinitionNode,
Expand Down Expand Up @@ -1184,6 +1185,7 @@ export class Parser {
* - UnionTypeExtension
* - EnumTypeExtension
* - InputObjectTypeDefinition
* - DirectiveDefinitionExtension
*/
parseTypeSystemExtension(): TypeSystemExtensionNode {
const keywordToken = this._lexer.lookahead();
Expand All @@ -1204,6 +1206,8 @@ export class Parser {
return this.parseEnumTypeExtension();
case 'input':
return this.parseInputObjectTypeExtension();
case 'directive':
return this.parseDirectiveDefinitionExtension();
}
}

Expand Down Expand Up @@ -1386,6 +1390,23 @@ export class Parser {
});
}

parseDirectiveDefinitionExtension(): DirectiveExtensionNode {
const start = this._lexer.token;
this.expectKeyword('extend');
this.expectKeyword('directive');
this.expectToken(TokenKind.AT);
const name = this.parseName();
const directives = this.parseConstDirectives();
if (directives.length === 0) {
throw this.unexpected();
}
return this.node<DirectiveExtensionNode>(start, {
kind: Kind.DIRECTIVE_EXTENSION,
name,
directives,
});
}

/**
* ```
* DirectiveDefinition :
Expand All @@ -1399,6 +1420,7 @@ export class Parser {
this.expectToken(TokenKind.AT);
const name = this.parseName();
const args = this.parseArgumentDefs();
const directives = this.parseConstDirectives();
const repeatable = this.expectOptionalKeyword('repeatable');
this.expectKeyword('on');
const locations = this.parseDirectiveLocations();
Expand All @@ -1407,6 +1429,7 @@ export class Parser {
description,
name,
arguments: args,
directives,
repeatable,
locations,
});
Expand Down Expand Up @@ -1447,6 +1470,7 @@ export class Parser {
* `ENUM_VALUE`
* `INPUT_OBJECT`
* `INPUT_FIELD_DEFINITION`
* `DIRECTIVE_DEFINITION`
*/
parseDirectiveLocation(): NameNode {
const start = this._lexer.token;
Expand Down
6 changes: 5 additions & 1 deletion src/language/predicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ export function isTypeDefinitionNode(
export function isTypeSystemExtensionNode(
node: ASTNode,
): node is TypeSystemExtensionNode {
return node.kind === Kind.SCHEMA_EXTENSION || isTypeExtensionNode(node);
return (
node.kind === Kind.SCHEMA_EXTENSION ||
node.kind === Kind.DIRECTIVE_EXTENSION ||
isTypeExtensionNode(node)
);
}

export function isTypeExtensionNode(node: ASTNode): node is TypeExtensionNode {
Expand Down
15 changes: 14 additions & 1 deletion src/language/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,21 @@ const printDocASTReducer: ASTReducer<string> = {
},

DirectiveDefinition: {
leave: ({ description, name, arguments: args, repeatable, locations }) =>
leave: ({
description,
name,
arguments: args,
directives,
repeatable,
locations,
}) =>
wrap('', description, '\n') +
'directive @' +
name +
(hasMultilineItems(args)
? wrap('(\n', indent(join(args, '\n')), '\n)')
: wrap('(', join(args, ', '), ')')) +
wrap(' ', join(directives, ' ')) +
(repeatable ? ' repeatable' : '') +
' on ' +
join(locations, ' | '),
Expand Down Expand Up @@ -311,6 +319,11 @@ const printDocASTReducer: ASTReducer<string> = {
join(['extend input', name, join(directives, ' '), block(fields)], ' '),
},

DirectiveExtension: {
leave: ({ name, directives }) =>
join(['extend directive @' + name, join(directives, ' ')], ' '),
},

// Schema Coordinates

TypeCoordinate: { leave: ({ name }) => name },
Expand Down
16 changes: 16 additions & 0 deletions src/type/__tests__/directive-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ describe('Type System: Directive', () => {
});
});

it('defines a deprecated directive', () => {
const directive = new GraphQLDirective({
name: 'Foo',
locations: [DirectiveLocation.QUERY],
deprecationReason: 'Some reason',
});

expect(directive).to.deep.include({
name: 'Foo',
args: [],
isRepeatable: false,
locations: ['QUERY'],
deprecationReason: 'Some reason',
});
});

it('can be stringified, JSON.stringified and Object.toStringified', () => {
const directive = new GraphQLDirective({
name: 'Foo',
Expand Down
Loading
Loading