Socket
Socket
Sign inDemoInstall

graphql-language-service-interface

Package Overview
Dependencies
5
Maintainers
13
Versions
122
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.9.6-canary-fae7d199.0 to 2.10.0-canary-033a43d2.0

4

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 const SuggestionCommand: {
command: string;
title: string;
};
export declare type AutocompleteSuggestionOptions = {

@@ -5,0 +9,0 @@ fillLeafsOnComplete?: boolean;

62

dist/getAutocompleteSuggestions.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTypeInfo = exports.canUseDirective = exports.runOnlineParser = exports.getTokenAtPosition = exports.getFragmentDefinitions = exports.getVariableCompletions = exports.getAutocompleteSuggestions = void 0;
exports.getTypeInfo = exports.canUseDirective = exports.runOnlineParser = exports.getTokenAtPosition = exports.getFragmentDefinitions = exports.getVariableCompletions = exports.getAutocompleteSuggestions = exports.SuggestionCommand = void 0;
const vscode_languageserver_types_1 = require("vscode-languageserver-types");

@@ -9,3 +9,6 @@ const graphql_1 = require("graphql");

const autocompleteUtils_1 = require("./autocompleteUtils");
const graphql_language_service_utils_1 = require("graphql-language-service-utils");
exports.SuggestionCommand = {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
};
const collectFragmentDefs = (op) => {

@@ -63,6 +66,3 @@ const externalFragments = [];

insertText: argDef.name + ': ',
command: {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
},
command: exports.SuggestionCommand,
detail: String(argDef.type),

@@ -133,31 +133,17 @@ documentation: (_a = argDef.description) !== null && _a !== void 0 ? _a : undefined,

const insertSuffix = ` {\n $1\n}`;
const appendInsertText = (field, type, options) => {
if (options === null || options === void 0 ? void 0 : options.fillLeafsOnComplete) {
const selectionSet = graphql_language_service_utils_1.buildSelectionSet(field, type, graphql_language_service_utils_1.defaultGetDefaultFieldNames, options.schema);
if (selectionSet) {
return ` ${graphql_1.print(selectionSet)}`;
}
}
return insertSuffix;
};
const getInsertText = (field, options) => {
var _a;
const getInsertText = (field) => {
const type = field.type;
if (graphql_2.isCompositeType(type)) {
return appendInsertText(field, type, options);
return insertSuffix;
}
if (graphql_1.isListType(type)) {
if ('ofType' in type) {
return appendInsertText(field, type === null || type === void 0 ? void 0 : type.ofType, options);
}
return insertSuffix;
}
if (graphql_1.isNonNullType(type)) {
if ('ofType' in type) {
if (graphql_1.isObjectType(type.ofType)) {
return appendInsertText(field, type.ofType, options);
}
if (graphql_1.isListType(type.ofType)) {
return appendInsertText(field, (_a = type === null || type === void 0 ? void 0 : type.ofType) === null || _a === void 0 ? void 0 : _a.ofType, options);
}
if (graphql_1.isObjectType(type.ofType)) {
return insertSuffix;
}
if (graphql_1.isListType(type.ofType)) {
return insertSuffix;
}
}

@@ -182,9 +168,5 @@ return null;

var _a;
if (field.name === 'securityAdvisories') {
console.log(field);
}
const suggestion = {
sortText: String(index) + field.name,
label: field.name,
insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.Snippet,
detail: String(field.type),

@@ -198,5 +180,7 @@ documentation: (_a = field.description) !== null && _a !== void 0 ? _a : undefined,

};
const insertText = getInsertText(field, options);
const insertText = getInsertText(field);
if (insertText) {
suggestion.insertText = field.name + insertText;
suggestion.insertTextFormat = vscode_languageserver_types_1.InsertTextFormat.Snippet;
suggestion.command = exports.SuggestionCommand;
}

@@ -691,3 +675,5 @@ return suggestion;

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

@@ -733,10 +719,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

@@ -7,3 +7,2 @@ import { DocumentNode, FragmentSpreadNode, NamedTypeNode } from 'graphql';

import { DefinitionQueryResult } from './getDefinition';
import { AutocompleteSuggestionOptions } from '.';
export declare class GraphQLLanguageService {

@@ -15,3 +14,3 @@ _graphQLCache: GraphQLCache;

getDiagnostics(query: string, uri: Uri, isRelayCompatMode?: boolean): Promise<Array<Diagnostic>>;
getAutocompleteSuggestions(query: string, position: IPosition, filePath: Uri, options?: AutocompleteSuggestionOptions): Promise<Array<CompletionItem>>;
getAutocompleteSuggestions(query: string, position: IPosition, filePath: Uri): Promise<Array<CompletionItem>>;
getHoverInformation(query: string, position: IPosition, filePath: Uri, options?: HoverConfig): Promise<Hover['contents']>;

@@ -18,0 +17,0 @@ getDefinition(query: string, position: IPosition, filePath: Uri): Promise<DefinitionQueryResult | null>;

@@ -124,3 +124,3 @@ "use strict";

}
getAutocompleteSuggestions(query, position, filePath, options) {
getAutocompleteSuggestions(query, position, filePath) {
return __awaiter(this, void 0, void 0, function* () {

@@ -132,3 +132,3 @@ const projectConfig = this.getConfigForURI(filePath);

if (schema) {
return getAutocompleteSuggestions_1.getAutocompleteSuggestions(schema, query, position, undefined, fragmentInfo, options);
return getAutocompleteSuggestions_1.getAutocompleteSuggestions(schema, query, position, undefined, fragmentInfo);
}

@@ -135,0 +135,0 @@ return [];

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 const SuggestionCommand: {
command: string;
title: string;
};
export declare type AutocompleteSuggestionOptions = {

@@ -5,0 +9,0 @@ fillLeafsOnComplete?: boolean;

import { CompletionItemKind, InsertTextFormat, } from 'vscode-languageserver-types';
import { isInterfaceType, GraphQLInterfaceType, GraphQLObjectType, Kind, DirectiveLocation, isObjectType, isListType, isNonNullType, print, } from 'graphql';
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';
import { CharacterStream, onlineParser, RuleKinds, } from 'graphql-language-service-parser';
import { forEachState, getDefinitionState, getFieldDef, hintList, objectValues, } from './autocompleteUtils';
import { buildSelectionSet, defaultGetDefaultFieldNames, } from 'graphql-language-service-utils';
export const SuggestionCommand = {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
};
const collectFragmentDefs = (op) => {

@@ -59,6 +62,3 @@ const externalFragments = [];

insertText: argDef.name + ': ',
command: {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
},
command: SuggestionCommand,
detail: String(argDef.type),

@@ -128,31 +128,17 @@ documentation: (_a = argDef.description) !== null && _a !== void 0 ? _a : undefined,

const insertSuffix = ` {\n $1\n}`;
const appendInsertText = (field, type, options) => {
if (options === null || options === void 0 ? void 0 : options.fillLeafsOnComplete) {
const selectionSet = buildSelectionSet(field, type, defaultGetDefaultFieldNames, options.schema);
if (selectionSet) {
return ` ${print(selectionSet)}`;
}
}
return insertSuffix;
};
const getInsertText = (field, options) => {
var _a;
const getInsertText = (field) => {
const type = field.type;
if (isCompositeType(type)) {
return appendInsertText(field, type, options);
return insertSuffix;
}
if (isListType(type)) {
if ('ofType' in type) {
return appendInsertText(field, type === null || type === void 0 ? void 0 : type.ofType, options);
}
return insertSuffix;
}
if (isNonNullType(type)) {
if ('ofType' in type) {
if (isObjectType(type.ofType)) {
return appendInsertText(field, type.ofType, options);
}
if (isListType(type.ofType)) {
return appendInsertText(field, (_a = type === null || type === void 0 ? void 0 : type.ofType) === null || _a === void 0 ? void 0 : _a.ofType, options);
}
if (isObjectType(type.ofType)) {
return insertSuffix;
}
if (isListType(type.ofType)) {
return insertSuffix;
}
}

@@ -177,9 +163,5 @@ return null;

var _a;
if (field.name === 'securityAdvisories') {
console.log(field);
}
const suggestion = {
sortText: String(index) + field.name,
label: field.name,
insertTextFormat: InsertTextFormat.Snippet,
detail: String(field.type),

@@ -193,5 +175,7 @@ documentation: (_a = field.description) !== null && _a !== void 0 ? _a : undefined,

};
const insertText = getInsertText(field, options);
const insertText = getInsertText(field);
if (insertText) {
suggestion.insertText = field.name + insertText;
suggestion.insertTextFormat = InsertTextFormat.Snippet;
suggestion.command = SuggestionCommand;
}

@@ -681,3 +665,5 @@ return suggestion;

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

@@ -722,10 +708,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

@@ -7,3 +7,2 @@ import { DocumentNode, FragmentSpreadNode, NamedTypeNode } from 'graphql';

import { DefinitionQueryResult } from './getDefinition';
import { AutocompleteSuggestionOptions } from '.';
export declare class GraphQLLanguageService {

@@ -15,3 +14,3 @@ _graphQLCache: GraphQLCache;

getDiagnostics(query: string, uri: Uri, isRelayCompatMode?: boolean): Promise<Array<Diagnostic>>;
getAutocompleteSuggestions(query: string, position: IPosition, filePath: Uri, options?: AutocompleteSuggestionOptions): Promise<Array<CompletionItem>>;
getAutocompleteSuggestions(query: string, position: IPosition, filePath: Uri): Promise<Array<CompletionItem>>;
getHoverInformation(query: string, position: IPosition, filePath: Uri, options?: HoverConfig): Promise<Hover['contents']>;

@@ -18,0 +17,0 @@ getDefinition(query: string, position: IPosition, filePath: Uri): Promise<DefinitionQueryResult | null>;

@@ -121,3 +121,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

}
getAutocompleteSuggestions(query, position, filePath, options) {
getAutocompleteSuggestions(query, position, filePath) {
return __awaiter(this, void 0, void 0, function* () {

@@ -129,3 +129,3 @@ const projectConfig = this.getConfigForURI(filePath);

if (schema) {
return getAutocompleteSuggestions(schema, query, position, undefined, fragmentInfo, options);
return getAutocompleteSuggestions(schema, query, position, undefined, fragmentInfo);
}

@@ -132,0 +132,0 @@ return [];

{
"name": "graphql-language-service-interface",
"version": "2.9.6-canary-fae7d199.0",
"version": "2.10.0-canary-033a43d2.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.7.0-canary-fae7d199.0",
"graphql-language-service-utils": "^2.6.3",
"vscode-languageserver-types": "^3.15.1"

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

@@ -22,4 +22,51 @@ /**

import { getAutocompleteSuggestions } from '../getAutocompleteSuggestions';
import {
getAutocompleteSuggestions,
SuggestionCommand,
} from '../getAutocompleteSuggestions';
const commonInsert = {
insertTextFormat: 2,
command: SuggestionCommand,
};
const expectedResults = {
droid: {
...commonInsert,
label: 'droid',
detail: 'Droid',
insertText: `droid {\n $1\n}`,
},
hero: {
...commonInsert,
label: 'hero',
detail: 'Character',
insertText: `hero {\n $1\n}`,
},
human: {
...commonInsert,
label: 'human',
detail: 'Human',
insertText: `human {\n $1\n}`,
},
inputTypeTest: {
...commonInsert,
label: 'inputTypeTest',
detail: 'TestType',
insertText: `inputTypeTest {\n $1\n}`,
},
appearsIn: {
...commonInsert,
label: 'appearsIn',
detail: '[Episode]',
insertText: `appearsIn {\n $1\n}`,
},
friends: {
...commonInsert,
label: 'friends',
detail: '[Character]',
insertText: `friends {\n $1\n}`,
},
};
const suggestionCommand = {

@@ -77,442 +124,458 @@ command: 'editor.action.triggerSuggest',

}
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!',
insertText: 'id: ',
command: suggestionCommand,
},
]);
});
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!',
command: suggestionCommand,
insertText: 'id: ',
},
]);
});
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', insertText: '$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: '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 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' },
]));
});

@@ -33,4 +33,2 @@ /**

isNonNullType,
GraphQLOutputType,
print,
} from 'graphql';

@@ -81,7 +79,8 @@

} from './autocompleteUtils';
import {
buildSelectionSet,
defaultGetDefaultFieldNames,
} from 'graphql-language-service-utils';
export const SuggestionCommand = {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
};
const collectFragmentDefs = (op: string | undefined) => {

@@ -186,6 +185,3 @@ const externalFragments: FragmentDefinitionNode[] = [];

insertText: argDef.name + ': ',
command: {
command: 'editor.action.triggerSuggest',
title: 'Suggestions',
},
command: SuggestionCommand,
detail: String(argDef.type),

@@ -299,48 +295,17 @@ documentation: argDef.description ?? undefined,

const appendInsertText = (
field: GraphQLField<null, null>,
type: GraphQLOutputType,
options?: AutocompleteSuggestionOptions,
) => {
if (options?.fillLeafsOnComplete) {
const selectionSet = buildSelectionSet(
field,
type,
defaultGetDefaultFieldNames,
options.schema,
);
if (selectionSet) {
return ` ${print(selectionSet)}`;
}
}
return insertSuffix;
};
const getInsertText = (
field: GraphQLField<null, null>,
options?: AutocompleteSuggestionOptions,
) => {
const getInsertText = (field: GraphQLField<null, null>) => {
const type = field.type;
if (isCompositeType(type)) {
return appendInsertText(field, type, options);
return insertSuffix;
}
if (isListType(type)) {
if ('ofType' in type) {
return appendInsertText(field, type?.ofType, options);
}
return insertSuffix;
}
if (isNonNullType(type)) {
if ('ofType' in type) {
if (isObjectType(type.ofType)) {
return appendInsertText(field, type.ofType, options);
}
if (isListType(type.ofType)) {
return appendInsertText(
field,
type?.ofType?.ofType as GraphQLOutputType,
options,
);
}
if (isObjectType(type.ofType)) {
return insertSuffix;
}
if (isListType(type.ofType)) {
return insertSuffix;
}
}

@@ -375,5 +340,2 @@ return null;

fields.map<CompletionItem>((field, index) => {
if (field.name === 'securityAdvisories') {
console.log(field);
}
const suggestion: CompletionItem = {

@@ -383,3 +345,2 @@ // This will sort the fields in the same order they are listed in the schema

label: field.name,
insertTextFormat: InsertTextFormat.Snippet,
detail: String(field.type),

@@ -393,7 +354,9 @@ documentation: field.description ?? undefined,

};
// TODO: fillLeafs capability
const insertText = getInsertText(field);
const insertText = getInsertText(field, options);
if (insertText) {
suggestion.insertText = field.name + insertText;
suggestion.insertTextFormat = InsertTextFormat.Snippet;
suggestion.command = SuggestionCommand;
}

@@ -1035,2 +998,3 @@ return suggestion;

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

@@ -1070,2 +1034,3 @@ const name = state.prevState && state.prevState.name;

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

@@ -1075,8 +1040,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:

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

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

@@ -1099,2 +1065,3 @@ const objectField =

inputType = objectField && objectField.type;
break;

@@ -1127,11 +1094,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;
}

@@ -50,3 +50,2 @@ /**

import { getASTNodeAtPosition } from 'graphql-language-service-utils';
import { AutocompleteSuggestionOptions } from '.';

@@ -228,3 +227,2 @@ const {

filePath: Uri,
options?: AutocompleteSuggestionOptions,
): Promise<Array<CompletionItem>> {

@@ -248,3 +246,2 @@ const projectConfig = this.getConfigForURI(filePath);

fragmentInfo,
options,
);

@@ -251,0 +248,0 @@ }

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc