Socket
Socket
Sign inDemoInstall

graphql-language-service-interface

Package Overview
Dependencies
Maintainers
13
Versions
122
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-language-service-interface - npm Package Compare versions

Comparing version 2.9.5 to 2.9.6-canary-1a140c54.0

6

dist/getAutocompleteSuggestions.d.ts
import { FragmentDefinitionNode, GraphQLDirective, GraphQLSchema } from 'graphql';
import { CompletionItem, AllTypeInfo, IPosition } from 'graphql-language-service-types';
import { CharacterStream, ContextToken, State, ContextTokenForCodeMirror } from 'graphql-language-service-parser';
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string): Array<CompletionItem>;
export declare type AutocompleteSuggestionOptions = {
fillLeafsOnComplete?: boolean;
schema?: GraphQLSchema;
};
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string, options?: AutocompleteSuggestionOptions): Array<CompletionItem>;
export declare function getVariableCompletions(queryText: string, schema: GraphQLSchema, token: ContextToken): CompletionItem[];

@@ -6,0 +10,0 @@ export declare function getFragmentDefinitions(queryText: string): Array<FragmentDefinitionNode>;

58

dist/getAutocompleteSuggestions.js

@@ -22,4 +22,5 @@ "use strict";

};
function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs) {
function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs, options) {
var _a;
const opts = Object.assign(Object.assign({}, options), { schema });
const token = contextToken || getTokenAtPosition(queryText, cursor);

@@ -50,3 +51,3 @@ const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state;

kind === graphql_language_service_parser_1.RuleKinds.ALIASED_FIELD) {
return getSuggestionsForFieldNames(token, typeInfo, schema);
return getSuggestionsForFieldNames(token, typeInfo, opts);
}

@@ -130,3 +131,23 @@ if (kind === graphql_language_service_parser_1.RuleKinds.ARGUMENTS ||

exports.getAutocompleteSuggestions = getAutocompleteSuggestions;
function getSuggestionsForFieldNames(token, typeInfo, schema) {
const insertSuffix = ` {\n $1\n}`;
const getInsertText = (field) => {
const type = field.type;
if (graphql_2.isCompositeType(type)) {
return insertSuffix;
}
if (graphql_1.isListType(type)) {
return insertSuffix;
}
if (graphql_1.isNonNullType(type)) {
if (graphql_1.isObjectType(type.ofType)) {
return insertSuffix;
}
if (graphql_1.isListType(type.ofType)) {
return insertSuffix;
}
}
return null;
};
function getSuggestionsForFieldNames(token, typeInfo, options) {
var _a;
if (typeInfo.parentType) {

@@ -141,3 +162,3 @@ const parentType = typeInfo.parentType;

}
if (parentType === schema.getQueryType()) {
if (parentType === ((_a = options === null || options === void 0 ? void 0 : options.schema) === null || _a === void 0 ? void 0 : _a.getQueryType())) {
fields.push(graphql_2.SchemaMetaFieldDef, graphql_2.TypeMetaFieldDef);

@@ -147,3 +168,6 @@ }

var _a;
return ({
if (field.name === 'securityAdvisories') {
console.log(field);
}
const suggestion = {
sortText: String(index) + field.name,

@@ -158,3 +182,9 @@ label: field.name,

type: field.type,
});
};
const insertText = getInsertText(field);
if (insertText) {
suggestion.insertText = field.name + insertText;
suggestion.insertTextFormat = vscode_languageserver_types_1.InsertTextFormat.Snippet;
}
return suggestion;
}));

@@ -357,6 +387,6 @@ }

if (!definitions[variableName]) {
const label = token.string === '$' ? variableName : '$' + variableName;
definitions[variableName] = {
detail: variableType.toString(),
label,
insertText: token.string === '$' ? variableName : '$' + variableName,
label: variableName,
type: variableType,

@@ -648,3 +678,5 @@ kind: vscode_languageserver_types_1.CompletionItemKind.Variable,

enumType instanceof graphql_2.GraphQLEnumType
? find(enumType.getValues(), (val) => val.value === state.name)
? enumType
.getValues()
.find((val) => val.value === state.name)
: null;

@@ -690,10 +722,2 @@ break;

exports.getTypeInfo = getTypeInfo;
function find(array, predicate) {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) {
return array[i];
}
}
return null;
}
//# sourceMappingURL=getAutocompleteSuggestions.js.map

@@ -6,4 +6,4 @@ export * from './autocompleteUtils';

export { getOutline } from './getOutline';
export { getHoverInformation } from './getHoverInformation';
export { getHoverInformation, HoverConfig } from './getHoverInformation';
export * from './GraphQLLanguageService';
//# sourceMappingURL=index.d.ts.map
import { FragmentDefinitionNode, GraphQLDirective, GraphQLSchema } from 'graphql';
import { CompletionItem, AllTypeInfo, IPosition } from 'graphql-language-service-types';
import { CharacterStream, ContextToken, State, ContextTokenForCodeMirror } from 'graphql-language-service-parser';
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string): Array<CompletionItem>;
export declare type AutocompleteSuggestionOptions = {
fillLeafsOnComplete?: boolean;
schema?: GraphQLSchema;
};
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string, options?: AutocompleteSuggestionOptions): Array<CompletionItem>;
export declare function getVariableCompletions(queryText: string, schema: GraphQLSchema, token: ContextToken): CompletionItem[];

@@ -6,0 +10,0 @@ export declare function getFragmentDefinitions(queryText: string): Array<FragmentDefinitionNode>;

@@ -1,3 +0,3 @@

import { CompletionItemKind } from 'vscode-languageserver-types';
import { isInterfaceType, GraphQLInterfaceType, GraphQLObjectType, Kind, DirectiveLocation, } from 'graphql';
import { CompletionItemKind, InsertTextFormat, } from 'vscode-languageserver-types';
import { isInterfaceType, GraphQLInterfaceType, GraphQLObjectType, Kind, DirectiveLocation, isObjectType, isListType, isNonNullType, } from 'graphql';
import { GraphQLBoolean, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, assertAbstractType, doTypesOverlap, getNamedType, getNullableType, isAbstractType, isCompositeType, isInputType, visit, parse, } from 'graphql';

@@ -19,4 +19,5 @@ import { CharacterStream, onlineParser, RuleKinds, } from 'graphql-language-service-parser';

};
export function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs) {
export function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs, options) {
var _a;
const opts = Object.assign(Object.assign({}, options), { schema });
const token = contextToken || getTokenAtPosition(queryText, cursor);

@@ -47,3 +48,3 @@ const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state;

kind === RuleKinds.ALIASED_FIELD) {
return getSuggestionsForFieldNames(token, typeInfo, schema);
return getSuggestionsForFieldNames(token, typeInfo, opts);
}

@@ -126,3 +127,23 @@ if (kind === RuleKinds.ARGUMENTS ||

}
function getSuggestionsForFieldNames(token, typeInfo, schema) {
const insertSuffix = ` {\n $1\n}`;
const getInsertText = (field) => {
const type = field.type;
if (isCompositeType(type)) {
return insertSuffix;
}
if (isListType(type)) {
return insertSuffix;
}
if (isNonNullType(type)) {
if (isObjectType(type.ofType)) {
return insertSuffix;
}
if (isListType(type.ofType)) {
return insertSuffix;
}
}
return null;
};
function getSuggestionsForFieldNames(token, typeInfo, options) {
var _a;
if (typeInfo.parentType) {

@@ -137,3 +158,3 @@ const parentType = typeInfo.parentType;

}
if (parentType === schema.getQueryType()) {
if (parentType === ((_a = options === null || options === void 0 ? void 0 : options.schema) === null || _a === void 0 ? void 0 : _a.getQueryType())) {
fields.push(SchemaMetaFieldDef, TypeMetaFieldDef);

@@ -143,3 +164,6 @@ }

var _a;
return ({
if (field.name === 'securityAdvisories') {
console.log(field);
}
const suggestion = {
sortText: String(index) + field.name,

@@ -154,3 +178,9 @@ label: field.name,

type: field.type,
});
};
const insertText = getInsertText(field);
if (insertText) {
suggestion.insertText = field.name + insertText;
suggestion.insertTextFormat = InsertTextFormat.Snippet;
}
return suggestion;
}));

@@ -353,6 +383,6 @@ }

if (!definitions[variableName]) {
const label = token.string === '$' ? variableName : '$' + variableName;
definitions[variableName] = {
detail: variableType.toString(),
label,
insertText: token.string === '$' ? variableName : '$' + variableName,
label: variableName,
type: variableType,

@@ -639,3 +669,5 @@ kind: CompletionItemKind.Variable,

enumType instanceof GraphQLEnumType
? find(enumType.getValues(), (val) => val.value === state.name)
? enumType
.getValues()
.find((val) => val.value === state.name)
: null;

@@ -680,10 +712,2 @@ break;

}
function find(array, predicate) {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) {
return array[i];
}
}
return null;
}
//# sourceMappingURL=getAutocompleteSuggestions.js.map

@@ -6,4 +6,4 @@ export * from './autocompleteUtils';

export { getOutline } from './getOutline';
export { getHoverInformation } from './getHoverInformation';
export { getHoverInformation, HoverConfig } from './getHoverInformation';
export * from './GraphQLLanguageService';
//# sourceMappingURL=index.d.ts.map
{
"name": "graphql-language-service-interface",
"version": "2.9.5",
"version": "2.9.6-canary-1a140c54.0",
"description": "Interface to the GraphQL Language Service",

@@ -34,3 +34,3 @@ "contributors": [

"graphql-language-service-types": "^1.8.6",
"graphql-language-service-utils": "^2.6.3",
"graphql-language-service-utils": "^2.7.0-canary-1a140c54.0",
"vscode-languageserver-types": "^3.15.1"

@@ -37,0 +37,0 @@ },

@@ -24,2 +24,46 @@ /**

const expectedResults = {
droid: {
label: 'droid',
detail: 'Droid',
insertTextFormat: 2,
insertText: `droid {\n $1\n}`,
},
hero: {
label: 'hero',
detail: 'Character',
insertTextFormat: 2,
insertText: `hero {\n $1\n}`,
},
human: {
label: 'human',
detail: 'Human',
insertTextFormat: 2,
insertText: `human {\n $1\n}`,
},
inputTypeTest: {
label: 'inputTypeTest',
detail: 'TestType',
insertTextFormat: 2,
insertText: `inputTypeTest {\n $1\n}`,
},
appearsIn: {
label: 'appearsIn',
detail: '[Episode]',
insertTextFormat: 2,
insertText: `appearsIn {\n $1\n}`,
},
friends: {
label: 'friends',
detail: '[Character]',
insertTextFormat: 2,
insertText: `friends {\n $1\n}`,
},
};
const suggestionCommand = {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
};
describe('getAutocompleteSuggestions', () => {

@@ -54,2 +98,4 @@ let schema: GraphQLSchema;

.map(suggestion => {
// TODO: A PR where we do `const { type, ..rest} = suggestion; return rest;`
// and validate the entire completion object - kinds, documentation, etc
const response = { label: suggestion.label } as CompletionItem;

@@ -59,432 +105,470 @@ if (suggestion.detail) {

}
// if(suggestion.documentation) {
// response.documentation = String(suggestion.documentation)
// }
if (suggestion.insertText) {
response.insertText = suggestion.insertText;
}
if (suggestion.insertTextFormat) {
response.insertTextFormat = suggestion.insertTextFormat;
}
if (suggestion.command) {
response.command = suggestion.command;
}
return response;
});
}
describe('with Operation types', () => {
it('provides correct sortText response', () => {
const result = getAutocompleteSuggestions(
schema,
`{ h`,
new Position(0, 3),
).map(({ sortText, label, detail }) => ({ sortText, label, detail }));
expect(result).toEqual([
{
sortText: '0hero',
label: 'hero',
detail: 'Character',
},
it('provides correct sortText response', () => {
const result = getAutocompleteSuggestions(
schema,
`{ h`,
new Position(0, 3),
).map(({ sortText, label, detail }) => ({ sortText, label, detail }));
expect(result).toEqual([
{
sortText: '0hero',
label: 'hero',
detail: 'Character',
},
{
sortText: '1human',
label: 'human',
detail: 'Human',
},
{
sortText: '1human',
label: 'human',
detail: 'Human',
},
{
sortText: '6__schema',
label: '__schema',
detail: '__Schema!',
},
]);
});
{
sortText: '6__schema',
label: '__schema',
detail: '__Schema!',
},
]);
});
it('provides correct initial keywords', () => {
expect(testSuggestions('', new Position(0, 0))).toEqual([
{ label: '{' },
{ label: 'fragment' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'subscription' },
]);
it('provides correct initial keywords', () => {
expect(testSuggestions('', new Position(0, 0))).toEqual([
{ label: '{' },
{ label: 'fragment' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'subscription' },
]);
expect(testSuggestions('q', new Position(0, 1))).toEqual([
{ label: '{' },
{ label: 'query' },
]);
});
expect(testSuggestions('q', new Position(0, 1))).toEqual([
{ label: '{' },
{ label: 'query' },
]);
});
it('provides correct suggestions at where the cursor is', () => {
// Below should provide initial keywords
expect(testSuggestions(' {}', new Position(0, 0))).toEqual([
{ label: '{' },
{ label: 'fragment' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'subscription' },
]);
it('provides correct suggestions at where the cursor is', () => {
// Below should provide initial keywords
expect(testSuggestions(' {}', new Position(0, 0))).toEqual([
{ label: '{' },
{ label: 'fragment' },
{ label: 'mutation' },
{ label: 'query' },
{ label: 'subscription' },
]);
// Below should provide root field names
expect(testSuggestions(' {}', new Position(0, 2))).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.droid,
expectedResults.hero,
expectedResults.human,
expectedResults.inputTypeTest,
]);
// Below should provide root field names
expect(testSuggestions(' {}', new Position(0, 2))).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'droid', detail: 'Droid' },
{ label: 'hero', detail: 'Character' },
{ label: 'human', detail: 'Human' },
{ label: 'inputTypeTest', detail: 'TestType' },
]);
// Test for query text with empty lines
expect(
testSuggestions(
`
query name {
...testFragment
}
`,
new Position(2, 0),
),
).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.droid,
expectedResults.hero,
expectedResults.human,
expectedResults.inputTypeTest,
]);
});
// Test for query text with empty lines
expect(
testSuggestions(
`
query name {
...testFragment
}
`,
new Position(2, 0),
),
).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'droid', detail: 'Droid' },
{ label: 'hero', detail: 'Character' },
{ label: 'human', detail: 'Human' },
{ label: 'inputTypeTest', detail: 'TestType' },
]);
});
it('provides correct field name suggestions', () => {
const result = testSuggestions('{ ', new Position(0, 2));
expect(result).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.droid,
expectedResults.hero,
expectedResults.human,
expectedResults.inputTypeTest,
]);
});
it('provides correct field name suggestions', () => {
const result = testSuggestions('{ ', new Position(0, 2));
expect(result).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'droid', detail: 'Droid' },
{ label: 'hero', detail: 'Character' },
{ label: 'human', detail: 'Human' },
{ label: 'inputTypeTest', detail: 'TestType' },
]);
});
it('provides correct field name suggestions after filtered', () => {
const result = testSuggestions('{ h ', new Position(0, 3));
expect(result).toEqual([expectedResults.hero, expectedResults.human]);
});
it('provides correct field name suggestions after filtered', () => {
const result = testSuggestions('{ h ', new Position(0, 3));
expect(result).toEqual([
{ label: 'hero', detail: 'Character' },
{ label: 'human', detail: 'Human' },
]);
});
it('provides correct field name suggestions with alias', () => {
const result = testSuggestions(
'{ alias: human(id: "1") { ',
new Position(0, 26),
);
it('provides correct field name suggestions with alias', () => {
const result = testSuggestions(
'{ alias: human(id: "1") { ',
new Position(0, 26),
);
expect(result).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
expect(result).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'appearsIn', detail: '[Episode]' },
{ label: 'friends', detail: '[Character]' },
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
it('provides correct field suggestions for fragments', () => {
const result = testSuggestions(
'fragment test on Human { ',
new Position(0, 25),
);
it('provides correct field suggestions for fragments', () => {
const result = testSuggestions(
'fragment test on Human { ',
new Position(0, 25),
);
expect(result).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
expect(result).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'appearsIn', detail: '[Episode]' },
{ label: 'friends', detail: '[Character]' },
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
it('provides correct argument suggestions', () => {
const result = testSuggestions('{ human (', new Position(0, 9));
expect(result).toEqual([
{
label: 'id',
detail: 'String!',
insertText: 'id: ',
command: suggestionCommand,
},
]);
});
it('provides correct argument suggestions', () => {
const result = testSuggestions('{ human (', new Position(0, 9));
expect(result).toEqual([{ label: 'id', detail: 'String!' }]);
});
it('provides correct argument suggestions when using aliases', () => {
const result = testSuggestions(
'{ aliasTest: human( ',
new Position(0, 20),
);
expect(result).toEqual([
{
label: 'id',
detail: 'String!',
command: suggestionCommand,
insertText: 'id: ',
},
]);
});
it('provides correct argument suggestions when using aliases', () => {
const result = testSuggestions('{ aliasTest: human( ', new Position(0, 20));
expect(result).toEqual([{ label: 'id', detail: 'String!' }]);
});
it('provides correct input type suggestions', () => {
const result = testSuggestions(
'query($exampleVariable: ) { ',
new Position(0, 24),
);
expect(result).toEqual([
{ label: '__DirectiveLocation' },
{ label: '__TypeKind' },
{ label: 'Boolean' },
{ label: 'Episode' },
{ label: 'InputType' },
{ label: 'Int' },
{ label: 'String' },
]);
});
it('provides correct input type suggestions', () => {
const result = testSuggestions(
'query($exampleVariable: ) { ',
new Position(0, 24),
);
expect(result).toEqual([
{ label: '__DirectiveLocation' },
{ label: '__TypeKind' },
{ label: 'Boolean' },
{ label: 'Episode' },
{ label: 'InputType' },
{ label: 'Int' },
{ label: 'String' },
]);
});
it('provides filtered input type suggestions', () => {
const result = testSuggestions(
'query($exampleVariable: In) { ',
new Position(0, 26),
);
expect(result).toEqual([
{ label: '__DirectiveLocation' },
{ label: '__TypeKind' },
{ label: 'InputType' },
{ label: 'Int' },
{ label: 'String' },
]);
});
it('provides filtered input type suggestions', () => {
const result = testSuggestions(
'query($exampleVariable: In) { ',
new Position(0, 26),
);
expect(result).toEqual([
{ label: '__DirectiveLocation' },
{ label: '__TypeKind' },
{ label: 'InputType' },
{ label: 'Int' },
{ label: 'String' },
]);
});
it('provides correct typeCondition suggestions', () => {
const suggestionsOnQuery = testSuggestions(
'{ ... on ',
new Position(0, 9),
);
expect(
suggestionsOnQuery.filter(({ label }) => !label.startsWith('__')),
).toEqual([{ label: 'Query' }]);
it('provides correct typeCondition suggestions', () => {
const suggestionsOnQuery = testSuggestions('{ ... on ', new Position(0, 9));
expect(
suggestionsOnQuery.filter(({ label }) => !label.startsWith('__')),
).toEqual([{ label: 'Query' }]);
const suggestionsOnCompositeType = testSuggestions(
'{ hero(episode: JEDI) { ... on } }',
new Position(0, 31),
);
const suggestionsOnCompositeType = testSuggestions(
'{ hero(episode: JEDI) { ... on } }',
new Position(0, 31),
);
expect(suggestionsOnCompositeType).toEqual([
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
]);
expect(suggestionsOnCompositeType).toEqual([
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
]);
expect(
testSuggestions(
'fragment Foo on Character { ... on }',
new Position(0, 35),
),
).toEqual([
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
]);
});
expect(
testSuggestions(
'fragment Foo on Character { ... on }',
new Position(0, 35),
),
).toEqual([{ label: 'Character' }, { label: 'Droid' }, { label: 'Human' }]);
});
it('provides correct typeCondition suggestions on fragment', () => {
const result = testSuggestions('fragment Foo on {}', new Position(0, 16));
expect(result.filter(({ label }) => !label.startsWith('__'))).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
{ label: 'Query' },
{ label: 'TestInterface' },
{ label: 'TestType' },
]);
});
it('provides correct typeCondition suggestions on fragment', () => {
const result = testSuggestions('fragment Foo on {}', new Position(0, 16));
expect(result.filter(({ label }) => !label.startsWith('__'))).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'Droid' },
{ label: 'Human' },
{ label: 'Query' },
{ label: 'TestInterface' },
{ label: 'TestType' },
]);
});
it('provides correct enum suggestions', () => {
const result = testSuggestions('{ hero(episode: ', new Position(0, 16));
expect(result).toEqual([
{ label: 'EMPIRE', detail: 'Episode' },
{ label: 'JEDI', detail: 'Episode' },
{ label: 'NEWHOPE', detail: 'Episode' },
]);
});
it('provides correct ENUM suggestions', () => {
const result = testSuggestions('{ hero(episode: ', new Position(0, 16));
expect(result).toEqual([
{ label: 'EMPIRE', detail: 'Episode' },
{ label: 'JEDI', detail: 'Episode' },
{ label: 'NEWHOPE', detail: 'Episode' },
]);
});
it('provides correct suggestions for declared variables upon typing $', () => {
const result = testSuggestions(
'query($id: String, $ep: Episode!){ hero(episode: $ }',
new Position(0, 51),
);
expect(result).toEqual([
{ label: 'ep', insertText: '$ep', detail: 'Episode' },
]);
});
it('provides correct suggestions for declared variables upon typing $', () => {
const result = testSuggestions(
'query($id: String, $ep: Episode!){ hero(episode: $ }',
new Position(0, 51),
);
expect(result).toEqual([{ label: '$ep', detail: 'Episode' }]);
});
it('provides correct suggestions for variables based on argument context', () => {
const result = testSuggestions(
'query($id: String!, $episode: Episode!){ hero(episode: ',
new Position(0, 55),
);
expect(result).toEqual([
{ label: 'EMPIRE', detail: 'Episode' },
{ label: 'episode', detail: 'Episode', insertText: '$episode' },
{ label: 'JEDI', detail: 'Episode' },
{ label: 'NEWHOPE', detail: 'Episode' },
// no $id here, it's not compatible :P
]);
});
it('provides correct suggestions for variables based on argument context', () => {
const result = testSuggestions(
'query($id: String!, $episode: Episode!){ hero(episode: ',
new Position(0, 55),
);
expect(result).toEqual([
{ label: '$episode', detail: 'Episode' },
{ label: 'EMPIRE', detail: 'Episode' },
{ label: 'JEDI', detail: 'Episode' },
{ label: 'NEWHOPE', detail: 'Episode' },
// no $id here, it's not compatible :P
]);
});
it('provides fragment name suggestion', () => {
const fragmentDef = 'fragment Foo on Human { id }';
it('provides fragment name suggestion', () => {
const fragmentDef = 'fragment Foo on Human { id }';
// Test on concrete types
expect(
testSuggestions(
`${fragmentDef} query { human(id: "1") { ...`,
new Position(0, 57),
),
).toEqual([{ label: 'Foo', detail: 'Human' }]);
expect(
testSuggestions(
`query { human(id: "1") { ... }} ${fragmentDef}`,
new Position(0, 28),
),
).toEqual([{ label: 'Foo', detail: 'Human' }]);
// Test on concrete types
expect(
testSuggestions(
`${fragmentDef} query { human(id: "1") { ...`,
new Position(0, 57),
),
).toEqual([{ label: 'Foo', detail: 'Human' }]);
expect(
testSuggestions(
`query { human(id: "1") { ... }} ${fragmentDef}`,
// Test on abstract type
expect(
testSuggestions(
`${fragmentDef} query { hero(episode: JEDI) { ...`,
new Position(0, 62),
),
).toEqual([{ label: 'Foo', detail: 'Human' }]);
});
it('provides correct fragment name suggestions for external fragments', () => {
const externalFragments = parse(`
fragment CharacterDetails on Human {
name
}
fragment CharacterDetails2 on Human {
name
}
`).definitions as FragmentDefinitionNode[];
const result = testSuggestions(
'query { human(id: "1") { ... }}',
new Position(0, 28),
),
).toEqual([{ label: 'Foo', detail: 'Human' }]);
externalFragments,
);
// Test on abstract type
expect(
testSuggestions(
`${fragmentDef} query { hero(episode: JEDI) { ...`,
new Position(0, 62),
),
).toEqual([{ label: 'Foo', detail: 'Human' }]);
});
expect(result).toEqual([
{ label: 'CharacterDetails', detail: 'Human' },
{ label: 'CharacterDetails2', detail: 'Human' },
]);
});
it('provides correct fragment name suggestions for external fragments', () => {
const externalFragments = parse(`
fragment CharacterDetails on Human {
name
}
fragment CharacterDetails2 on Human {
name
}
`).definitions as FragmentDefinitionNode[];
it('provides correct directive suggestions', () => {
expect(testSuggestions('{ test @ }', new Position(0, 8))).toEqual([
{ label: 'include' },
{ label: 'skip' },
{ label: 'stream' },
{ label: 'test' },
]);
const result = testSuggestions(
'query { human(id: "1") { ... }}',
new Position(0, 28),
externalFragments,
);
expect(testSuggestions('{ test @', new Position(0, 8))).toEqual([
{ label: 'include' },
{ label: 'skip' },
{ label: 'stream' },
{ label: 'test' },
]);
expect(result).toEqual([
{ label: 'CharacterDetails', detail: 'Human' },
{ label: 'CharacterDetails2', detail: 'Human' },
]);
});
expect(
testSuggestions('{ aliasTest: test @ }', new Position(0, 19)),
).toEqual([
{ label: 'include' },
{ label: 'skip' },
{ label: 'stream' },
{ label: 'test' },
]);
it('provides correct directive suggestions', () => {
expect(testSuggestions('{ test @ }', new Position(0, 8))).toEqual([
{ label: 'include' },
{ label: 'skip' },
{ label: 'stream' },
{ label: 'test' },
]);
expect(testSuggestions('query @', new Position(0, 7))).toEqual([]);
});
expect(testSuggestions('{ test @', new Position(0, 8))).toEqual([
{ label: 'include' },
{ label: 'skip' },
{ label: 'stream' },
{ label: 'test' },
]);
it('provides correct testInput suggestions', () => {
expect(
testSuggestions('{ inputTypeTest(args: {', new Position(0, 23)),
).toEqual([
{ label: 'key', detail: 'String!' },
{ label: 'value', detail: 'Int' },
]);
});
expect(
testSuggestions('{ aliasTest: test @ }', new Position(0, 19)),
).toEqual([
{ label: 'include' },
{ label: 'skip' },
{ label: 'stream' },
{ label: 'test' },
]);
it('provides correct field name suggestion inside inline fragment', () => {
expect(
testSuggestions(
'fragment Foo on Character { ... on Human { }}',
new Position(0, 42),
),
).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
expect(testSuggestions('query @', new Position(0, 7))).toEqual([]);
// Typeless inline fragment assumes the type automatically
expect(
testSuggestions('fragment Foo on Droid { ... { ', new Position(0, 30)),
).toEqual([
{ label: '__typename', detail: 'String!' },
expectedResults.appearsIn,
expectedResults.friends,
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'primaryFunction', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
});
});
describe('with SDL types', () => {
it('provides correct directive suggestions on definitions', () =>
expect(testSuggestions('type Type @', new Position(0, 11))).toEqual([
{ label: 'onAllDefs' },
]));
it('provides correct suggestions on object fields', () =>
expect(
testSuggestions(`type Type {\n aField: s`, new Position(0, 23)),
).toEqual([{ label: 'Episode' }, { label: 'String' }]));
it('provides correct suggestions on input object fields', () =>
expect(
testSuggestions(`input Type {\n aField: s`, new Position(0, 23)),
).toEqual([{ label: 'Episode' }, { label: 'String' }]));
it('provides correct directive suggestions on args definitions', () =>
expect(
testSuggestions('type Type { field(arg: String @', new Position(0, 31)),
).toEqual([
{ label: 'deprecated' },
{ label: 'onAllDefs' },
{ label: 'onArg' },
]));
it('provides correct testInput suggestions', () => {
expect(
testSuggestions('{ inputTypeTest(args: {', new Position(0, 23)),
).toEqual([
{ label: 'key', detail: 'String!' },
{ label: 'value', detail: 'Int' },
]);
});
it('provides correct interface suggestions when extending with an interface', () =>
expect(
testSuggestions('type Type implements ', new Position(0, 20)),
).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
]));
it('provides correct field name suggestion inside inline fragment', () => {
expect(
testSuggestions(
'fragment Foo on Character { ... on Human { }}',
new Position(0, 42),
),
).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'appearsIn', detail: '[Episode]' },
{ label: 'friends', detail: '[Character]' },
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
// Typeless inline fragment assumes the type automatically
expect(
testSuggestions('fragment Foo on Droid { ... { ', new Position(0, 30)),
).toEqual([
{ label: '__typename', detail: 'String!' },
{ label: 'appearsIn', detail: '[Episode]' },
{ label: 'friends', detail: '[Character]' },
{ label: 'id', detail: 'String!' },
{ label: 'name', detail: 'String' },
{ label: 'primaryFunction', detail: 'String' },
{ label: 'secretBackstory', detail: 'String' },
]);
it('provides correct interface suggestions when extending a type with multiple interfaces', () =>
expect(
testSuggestions(
'type Type implements TestInterface & ',
new Position(0, 37),
),
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }]));
it('provides correct interface suggestions when extending an interface with multiple interfaces', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & ',
new Position(0, 46),
),
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }]));
it('provides filtered interface suggestions when extending an interface with multiple interfaces', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & Inter',
new Position(0, 48),
),
).toEqual([{ label: 'AnotherInterface' }]));
it('provides no interface suggestions when using implements and there are no & or { characters present', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface ',
new Position(0, 44),
),
).toEqual([]));
it('provides fragment completion after a list of interfaces to extend', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & AnotherInterface @f',
new Position(0, 65),
),
).toEqual([{ label: 'onAllDefs' }]));
it('provides correct interface suggestions when extending an interface with an inline interface', () =>
expect(
testSuggestions(
'interface A { id: String }\ninterface MyInterface implements ',
new Position(1, 33),
),
).toEqual([
{ label: 'A' },
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
]));
});
it('provides correct directive suggestions on definitions', () =>
expect(testSuggestions('type Type @', new Position(0, 11))).toEqual([
{ label: 'onAllDefs' },
]));
it('provides correct directive suggestions on args definitions', () =>
expect(
testSuggestions('type Type { field(arg: String @', new Position(0, 31)),
).toEqual([
{ label: 'deprecated' },
{ label: 'onAllDefs' },
{ label: 'onArg' },
]));
it('provides correct interface suggestions when extending with an interface', () =>
expect(
testSuggestions('type Type implements ', new Position(0, 20)),
).toEqual([
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
]));
it('provides correct interface suggestions when extending a type with multiple interfaces', () =>
expect(
testSuggestions(
'type Type implements TestInterface & ',
new Position(0, 37),
),
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }]));
it('provides correct interface suggestions when extending an interface with multiple interfaces', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & ',
new Position(0, 46),
),
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }]));
it('provides filtered interface suggestions when extending an interface with multiple interfaces', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & Inter',
new Position(0, 48),
),
).toEqual([{ label: 'AnotherInterface' }]));
it('provides no interface suggestions when using implements and there are no & or { characters present', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface ',
new Position(0, 44),
),
).toEqual([]));
it('provides fragment completion after a list of interfaces to extend', () =>
expect(
testSuggestions(
'interface IExample implements TestInterface & AnotherInterface @f',
new Position(0, 65),
),
).toEqual([{ label: 'onAllDefs' }]));
it('provides correct interface suggestions when extending an interface with an inline interface', () =>
expect(
testSuggestions(
'interface A { id: String }\ninterface MyInterface implements ',
new Position(1, 33),
),
).toEqual([
{ label: 'A' },
{ label: 'AnotherInterface' },
{ label: 'Character' },
{ label: 'TestInterface' },
]));
});

@@ -9,3 +9,6 @@ /**

*/
import { CompletionItemKind } from 'vscode-languageserver-types';
import {
CompletionItemKind,
InsertTextFormat,
} from 'vscode-languageserver-types';

@@ -28,2 +31,5 @@ import {

GraphQLArgument,
isObjectType,
isListType,
isNonNullType,
} from 'graphql';

@@ -92,2 +98,7 @@

export type AutocompleteSuggestionOptions = {
fillLeafsOnComplete?: boolean;
schema?: GraphQLSchema;
};
/**

@@ -103,3 +114,8 @@ * Given GraphQLSchema, queryText, and context of the current position within

fragmentDefs?: FragmentDefinitionNode[] | string,
options?: AutocompleteSuggestionOptions,
): Array<CompletionItem> {
const opts = {
...options,
schema,
};
const token: ContextToken =

@@ -118,3 +134,2 @@ contextToken || getTokenAtPosition(queryText, cursor);

const step = state.step;
const typeInfo = getTypeInfo(schema, token.state);

@@ -152,3 +167,3 @@ // Definition kinds

) {
return getSuggestionsForFieldNames(token, typeInfo, schema);
return getSuggestionsForFieldNames(token, typeInfo, opts);
}

@@ -279,2 +294,23 @@

const insertSuffix = ` {\n $1\n}`;
const getInsertText = (field: GraphQLField<null, null>) => {
const type = field.type;
if (isCompositeType(type)) {
return insertSuffix;
}
if (isListType(type)) {
return insertSuffix;
}
if (isNonNullType(type)) {
if (isObjectType(type.ofType)) {
return insertSuffix;
}
if (isListType(type.ofType)) {
return insertSuffix;
}
}
return null;
};
// Helper functions to get suggestions for each kinds

@@ -284,3 +320,3 @@ function getSuggestionsForFieldNames(

typeInfo: AllTypeInfo,
schema: GraphQLSchema,
options?: AutocompleteSuggestionOptions,
): Array<CompletionItem> {

@@ -300,3 +336,3 @@ if (typeInfo.parentType) {

}
if (parentType === schema.getQueryType()) {
if (parentType === options?.schema?.getQueryType()) {
fields.push(SchemaMetaFieldDef, TypeMetaFieldDef);

@@ -306,14 +342,27 @@ }

token,
fields.map<CompletionItem>((field, index) => ({
// This will sort the fields in the same order they are listed in the schema
sortText: String(index) + field.name,
label: field.name,
detail: String(field.type),
documentation: field.description ?? undefined,
deprecated: Boolean(field.deprecationReason),
isDeprecated: Boolean(field.deprecationReason),
deprecationReason: field.deprecationReason,
kind: CompletionItemKind.Field,
type: field.type,
})),
fields.map<CompletionItem>((field, index) => {
if (field.name === 'securityAdvisories') {
console.log(field);
}
const suggestion: CompletionItem = {
// This will sort the fields in the same order they are listed in the schema
sortText: String(index) + field.name,
label: field.name,
detail: String(field.type),
documentation: field.description ?? undefined,
deprecated: Boolean(field.deprecationReason),
isDeprecated: Boolean(field.deprecationReason),
deprecationReason: field.deprecationReason,
kind: CompletionItemKind.Field,
type: field.type,
};
// TODO: fillLeafs capability
const insertText = getInsertText(field);
if (insertText) {
suggestion.insertText = field.name + insertText;
suggestion.insertTextFormat = InsertTextFormat.Snippet;
}
return suggestion;
}),
);

@@ -625,6 +674,7 @@ }

// append `$` if the `token.string` is not already `$`
const label = token.string === '$' ? variableName : '$' + variableName;
definitions[variableName] = {
detail: variableType.toString(),
label,
insertText: token.string === '$' ? variableName : '$' + variableName,
label: variableName, // keep label the same for `codemirror-graphql`
type: variableType,

@@ -952,2 +1002,3 @@ kind: CompletionItemKind.Variable,

break;
// TODO: needs more tests
case RuleKinds.ALIASED_FIELD: {

@@ -987,2 +1038,3 @@ const name = state.prevState && state.prevState.name;

break;
// TODO: needs tests
case RuleKinds.ENUM_VALUE:

@@ -992,8 +1044,8 @@ const enumType = getNamedType(inputType as GraphQLType);

enumType instanceof GraphQLEnumType
? find(
enumType.getValues() as GraphQLEnumValue[],
(val: GraphQLEnumValue) => val.value === state.name,
)
? enumType
.getValues()
.find((val: GraphQLEnumValue) => val.value === state.name)
: null;
break;
// TODO: needs tests
case RuleKinds.LIST_VALUE:

@@ -1011,2 +1063,3 @@ const nullableType = getNullableType(inputType as GraphQLType);

break;
// TODO: needs tests
case RuleKinds.OBJECT_FIELD:

@@ -1016,2 +1069,3 @@ const objectField =

inputType = objectField && objectField.type;
break;

@@ -1044,11 +1098,1 @@ case RuleKinds.NAMED_TYPE:

}
// Returns the first item in the array which causes predicate to return truthy.
function find(array: any[], predicate: Function) {
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) {
return array[i];
}
}
return null;
}

@@ -19,4 +19,4 @@ /**

export { getOutline } from './getOutline';
export { getHoverInformation } from './getHoverInformation';
export { getHoverInformation, HoverConfig } from './getHoverInformation';
export * from './GraphQLLanguageService';

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc