Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
9 changes: 9 additions & 0 deletions .changeset/afraid-oranges-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'graphql-language-service': patch
---

fix: Correctly parse schema extensions with no root operations

Previously, the parser gave schema extensions the same treatment as schema definitions. The requirements are slightly different, however, since a schema extension does not require a list of root operations according to the spec: https://spec.graphql.org/draft/#sec-Schema-Extension.

The rule for parsing a schema extension is now distinct from schema definition, allowing the root operations list to be omitted.
16 changes: 7 additions & 9 deletions packages/graphql-language-service/src/parser/Rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,8 @@ export const ParseRules: { [name: string]: ParseRule } = {
Implements: [word('implements'), list('NamedType', p('&'))],
DirectiveLocation: [name('string-2')],
// GraphQL schema language
SchemaDef: [
word('schema'),
list('Directive'),
p('{'),
list('OperationTypeDef'),
p('}'),
],

SchemaDef: [word('schema'), list('Directive'), 'OperationTypeDefs'],
OperationTypeDefs: [p('{'), list('OperationTypeDef'), p('}')],
OperationTypeDef: [name('keyword'), p(':'), name('atom')],
ScalarDef: [word('scalar'), name('atom'), list('Directive')],
ObjectTypeDef: [
Expand Down Expand Up @@ -322,7 +316,11 @@ export const ParseRules: { [name: string]: ParseRule } = {
return Kind.INPUT_OBJECT_TYPE_EXTENSION;
}
},
[Kind.SCHEMA_EXTENSION]: ['SchemaDef'],
[Kind.SCHEMA_EXTENSION]: [
word('schema'),
list('Directive'),
opt('OperationTypeDefs'),
],
[Kind.SCALAR_TYPE_EXTENSION]: ['ScalarDef'],
[Kind.OBJECT_TYPE_EXTENSION]: ['ObjectTypeDef'],
[Kind.INTERFACE_TYPE_EXTENSION]: ['InterfaceDef'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('onlineParser', () => {
`);

t.keyword('schema', { kind: 'SchemaDef' });
t.punctuation('{');
t.punctuation('{', { kind: 'OperationTypeDefs' });

t.keyword('query', { kind: 'OperationTypeDef' });
t.punctuation(':');
Expand All @@ -67,6 +67,71 @@ describe('onlineParser', () => {
t.eol();
});

it('parses schema extension bare', () => {
const { t } = getUtils(`
extend schema
`);

t.keyword('extend', { kind: 'ExtendDef' });
t.keyword('schema', { kind: 'SchemaExtension' });

t.eol();
});

it('parses schema extension with operation defs', () => {
const { t } = getUtils(`
extend schema {
query: SomeType
}
`);

t.keyword('extend', { kind: 'ExtendDef' });
t.keyword('schema', { kind: 'SchemaExtension' });
t.punctuation('{', { kind: 'OperationTypeDefs' });

t.keyword('query', { kind: 'OperationTypeDef' });
t.punctuation(':');
t.name('SomeType');

t.punctuation('}', { kind: 'Document' });

t.eol();
});

it('parses schema extension with directive applications', () => {
const { t } = getUtils(`
extend schema @someDirective
`);

t.keyword('extend', { kind: 'ExtendDef' });
t.keyword('schema', { kind: 'SchemaExtension' });
expectDirective({ t }, { name: 'someDirective' });

t.eol();
});

it('parses schema extension with directive applications without root operation definitions, followed by a type definition', () => {
const { t } = getUtils(`
extend schema @someDirective

type A { field: String }
`);

t.keyword('extend', { kind: 'ExtendDef' });
t.keyword('schema', { kind: 'SchemaExtension' });
expectDirective({ t }, { name: 'someDirective' });

t.keyword('type', { kind: 'ObjectTypeDef' });
t.name('A');
t.punctuation('{');
t.property('field', { kind: 'FieldDef' });
t.punctuation(':');
t.name('String', { kind: 'NamedType' });
t.punctuation('}', { kind: 'Document' });

t.eol();
});

it('parses short query', () => {
const { t } = getUtils(`
{
Expand Down