UNPKG

@platformos/pos-cli

Version:

Manage your platformOS application

461 lines 17.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const graphql_1 = require("graphql"); const graphql_language_service_parser_1 = require("graphql-language-service-parser"); const autocompleteUtils_1 = require("./autocompleteUtils"); function getAutocompleteSuggestions(schema, queryText, cursor, contextToken) { const token = contextToken || getTokenAtPosition(queryText, cursor); const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state; if (!state) { return []; } const kind = state.kind; const step = state.step; const typeInfo = getTypeInfo(schema, token.state); if (kind === 'Document') { return autocompleteUtils_1.hintList(token, [ { label: 'query' }, { label: 'mutation' }, { label: 'subscription' }, { label: 'fragment' }, { label: '{' }, ]); } if (kind === 'SelectionSet' || kind === 'Field' || kind === 'AliasedField') { return getSuggestionsForFieldNames(token, typeInfo, schema); } if (kind === 'Arguments' || (kind === 'Argument' && step === 0)) { const argDefs = typeInfo.argDefs; if (argDefs) { return autocompleteUtils_1.hintList(token, argDefs.map(argDef => ({ label: argDef.name, detail: String(argDef.type), documentation: argDef.description, }))); } } if (kind === 'ObjectValue' || (kind === 'ObjectField' && step === 0)) { if (typeInfo.objectFieldDefs) { const objectFields = autocompleteUtils_1.objectValues(typeInfo.objectFieldDefs); return autocompleteUtils_1.hintList(token, objectFields.map(field => ({ label: field.name, detail: String(field.type), documentation: field.description, }))); } } if (kind === 'EnumValue' || (kind === 'ListValue' && step === 1) || (kind === 'ObjectField' && step === 2) || (kind === 'Argument' && step === 2)) { return getSuggestionsForInputValues(token, typeInfo); } if ((kind === 'TypeCondition' && step === 1) || (kind === 'NamedType' && state.prevState != null && state.prevState.kind === 'TypeCondition')) { return getSuggestionsForFragmentTypeConditions(token, typeInfo, schema); } if (kind === 'FragmentSpread' && step === 1) { return getSuggestionsForFragmentSpread(token, typeInfo, schema, queryText); } if ((kind === 'VariableDefinition' && step === 2) || (kind === 'ListType' && step === 1) || (kind === 'NamedType' && state.prevState && (state.prevState.kind === 'VariableDefinition' || state.prevState.kind === 'ListType'))) { return getSuggestionsForVariableDefinition(token, schema); } if (kind === 'Directive') { return getSuggestionsForDirective(token, state, schema); } return []; } exports.getAutocompleteSuggestions = getAutocompleteSuggestions; function getSuggestionsForFieldNames(token, typeInfo, schema) { if (typeInfo.parentType) { const parentType = typeInfo.parentType; const fields = 'getFields' in parentType ? autocompleteUtils_1.objectValues(parentType.getFields()) : []; if (graphql_1.isCompositeType(parentType)) { fields.push(graphql_1.TypeNameMetaFieldDef); } if (parentType === schema.getQueryType()) { fields.push(graphql_1.SchemaMetaFieldDef, graphql_1.TypeMetaFieldDef); } return autocompleteUtils_1.hintList(token, fields.map((field, index) => ({ sortText: String(index) + field.name, label: field.name, detail: String(field.type), documentation: field.description, isDeprecated: field.isDeprecated, deprecationReason: field.deprecationReason, }))); } return []; } function getSuggestionsForInputValues(token, typeInfo) { const namedInputType = graphql_1.getNamedType(typeInfo.inputType); if (namedInputType instanceof graphql_1.GraphQLEnumType) { const values = namedInputType.getValues(); return autocompleteUtils_1.hintList(token, values.map(value => ({ label: value.name, detail: String(namedInputType), documentation: value.description, isDeprecated: value.isDeprecated, deprecationReason: value.deprecationReason, }))); } else if (namedInputType === graphql_1.GraphQLBoolean) { return autocompleteUtils_1.hintList(token, [ { label: 'true', detail: String(graphql_1.GraphQLBoolean), documentation: 'Not false.', }, { label: 'false', detail: String(graphql_1.GraphQLBoolean), documentation: 'Not true.', }, ]); } return []; } function getSuggestionsForFragmentTypeConditions(token, typeInfo, schema) { let possibleTypes; if (typeInfo.parentType) { if (graphql_1.isAbstractType(typeInfo.parentType)) { const abstractType = graphql_1.assertAbstractType(typeInfo.parentType); const possibleObjTypes = schema.getPossibleTypes(abstractType); const possibleIfaceMap = Object.create(null); possibleObjTypes.forEach(type => { type.getInterfaces().forEach(iface => { possibleIfaceMap[iface.name] = iface; }); }); possibleTypes = possibleObjTypes.concat(autocompleteUtils_1.objectValues(possibleIfaceMap)); } else { possibleTypes = [typeInfo.parentType]; } } else { const typeMap = schema.getTypeMap(); possibleTypes = autocompleteUtils_1.objectValues(typeMap).filter(graphql_1.isCompositeType); } return autocompleteUtils_1.hintList(token, possibleTypes.map((type) => { const namedType = graphql_1.getNamedType(type); return { label: String(type), documentation: (namedType && namedType.description) || '', }; })); } function getSuggestionsForFragmentSpread(token, typeInfo, schema, queryText) { const typeMap = schema.getTypeMap(); const defState = autocompleteUtils_1.getDefinitionState(token.state); const fragments = getFragmentDefinitions(queryText); const relevantFrags = fragments.filter(frag => typeMap[frag.typeCondition.name.value] && !(defState && defState.kind === 'FragmentDefinition' && defState.name === frag.name.value) && graphql_1.isCompositeType(typeInfo.parentType) && graphql_1.isCompositeType(typeMap[frag.typeCondition.name.value]) && graphql_1.doTypesOverlap(schema, typeInfo.parentType, typeMap[frag.typeCondition.name.value])); return autocompleteUtils_1.hintList(token, relevantFrags.map(frag => ({ label: frag.name.value, detail: String(typeMap[frag.typeCondition.name.value]), documentation: `fragment ${frag.name.value} on ${frag.typeCondition.name.value}`, }))); } function getFragmentDefinitions(queryText) { const fragmentDefs = []; runOnlineParser(queryText, (_, state) => { if (state.kind === 'FragmentDefinition' && state.name && state.type) { fragmentDefs.push({ kind: 'FragmentDefinition', name: { kind: 'Name', value: state.name, }, selectionSet: { kind: 'SelectionSet', selections: [], }, typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: state.type, }, }, }); } }); return fragmentDefs; } function getSuggestionsForVariableDefinition(token, schema) { const inputTypeMap = schema.getTypeMap(); const inputTypes = autocompleteUtils_1.objectValues(inputTypeMap).filter(graphql_1.isInputType); return autocompleteUtils_1.hintList(token, inputTypes.map((type) => ({ label: type.name, documentation: type.description, }))); } function getSuggestionsForDirective(token, state, schema) { if (state.prevState && state.prevState.kind) { const directives = schema .getDirectives() .filter(directive => canUseDirective(state.prevState, directive)); return autocompleteUtils_1.hintList(token, directives.map(directive => ({ label: directive.name, documentation: directive.description || '', }))); } return []; } function getTokenAtPosition(queryText, cursor) { let styleAtCursor = null; let stateAtCursor = null; let stringAtCursor = null; const token = runOnlineParser(queryText, (stream, state, style, index) => { if (index === cursor.line) { if (stream.getCurrentPosition() >= cursor.character) { styleAtCursor = style; stateAtCursor = { ...state }; stringAtCursor = stream.current(); return 'BREAK'; } } }); return { start: token.start, end: token.end, string: stringAtCursor || token.string, state: stateAtCursor || token.state, style: styleAtCursor || token.style, }; } exports.getTokenAtPosition = getTokenAtPosition; function runOnlineParser(queryText, callback) { const lines = queryText.split('\n'); const parser = graphql_language_service_parser_1.onlineParser(); let state = parser.startState(); let style = ''; let stream = new graphql_language_service_parser_1.CharacterStream(''); for (let i = 0; i < lines.length; i++) { stream = new graphql_language_service_parser_1.CharacterStream(lines[i]); while (!stream.eol()) { style = parser.token(stream, state); const code = callback(stream, state, style, i); if (code === 'BREAK') { break; } } callback(stream, state, style, i); if (!state.kind) { state = parser.startState(); } } return { start: stream.getStartOfToken(), end: stream.getCurrentPosition(), string: stream.current(), state, style, }; } function canUseDirective(state, directive) { if (!state || !state.kind) { return false; } const kind = state.kind; const locations = directive.locations; switch (kind) { case 'Query': return locations.indexOf('QUERY') !== -1; case 'Mutation': return locations.indexOf('MUTATION') !== -1; case 'Subscription': return locations.indexOf('SUBSCRIPTION') !== -1; case 'Field': case 'AliasedField': return locations.indexOf('FIELD') !== -1; case 'FragmentDefinition': return locations.indexOf('FRAGMENT_DEFINITION') !== -1; case 'FragmentSpread': return locations.indexOf('FRAGMENT_SPREAD') !== -1; case 'InlineFragment': return locations.indexOf('INLINE_FRAGMENT') !== -1; case 'SchemaDef': return locations.indexOf('SCHEMA') !== -1; case 'ScalarDef': return locations.indexOf('SCALAR') !== -1; case 'ObjectTypeDef': return locations.indexOf('OBJECT') !== -1; case 'FieldDef': return locations.indexOf('FIELD_DEFINITION') !== -1; case 'InterfaceDef': return locations.indexOf('INTERFACE') !== -1; case 'UnionDef': return locations.indexOf('UNION') !== -1; case 'EnumDef': return locations.indexOf('ENUM') !== -1; case 'EnumValue': return locations.indexOf('ENUM_VALUE') !== -1; case 'InputDef': return locations.indexOf('INPUT_OBJECT') !== -1; case 'InputValueDef': const prevStateKind = state.prevState && state.prevState.kind; switch (prevStateKind) { case 'ArgumentsDef': return locations.indexOf('ARGUMENT_DEFINITION') !== -1; case 'InputDef': return locations.indexOf('INPUT_FIELD_DEFINITION') !== -1; } } return false; } function getTypeInfo(schema, tokenState) { let argDef; let argDefs; let directiveDef; let enumValue; let fieldDef; let inputType; let objectFieldDefs; let parentType; let type; autocompleteUtils_1.forEachState(tokenState, state => { switch (state.kind) { case 'Query': case 'ShortQuery': type = schema.getQueryType(); break; case 'Mutation': type = schema.getMutationType(); break; case 'Subscription': type = schema.getSubscriptionType(); break; case 'InlineFragment': case 'FragmentDefinition': if (state.type) { type = schema.getType(state.type); } break; case 'Field': case 'AliasedField': if (!type || !state.name) { fieldDef = null; } else { fieldDef = parentType ? autocompleteUtils_1.getFieldDef(schema, parentType, state.name) : null; type = fieldDef ? fieldDef.type : null; } break; case 'SelectionSet': parentType = graphql_1.getNamedType(type); break; case 'Directive': directiveDef = state.name ? schema.getDirective(state.name) : null; break; case 'Arguments': if (!state.prevState) { argDefs = null; } else { switch (state.prevState.kind) { case 'Field': argDefs = fieldDef && fieldDef.args; break; case 'Directive': argDefs = directiveDef && directiveDef.args; break; case 'AliasedField': const name = state.prevState && state.prevState.name; if (!name) { argDefs = null; break; } const field = parentType ? autocompleteUtils_1.getFieldDef(schema, parentType, name) : null; if (!field) { argDefs = null; break; } argDefs = field.args; break; default: argDefs = null; break; } } break; case 'Argument': if (argDefs) { for (let i = 0; i < argDefs.length; i++) { if (argDefs[i].name === state.name) { argDef = argDefs[i]; break; } } } inputType = argDef && argDef.type; break; case 'EnumValue': const enumType = graphql_1.getNamedType(inputType); enumValue = enumType instanceof graphql_1.GraphQLEnumType ? find(enumType.getValues(), (val) => val.value === state.name) : null; break; case 'ListValue': const nullableType = graphql_1.getNullableType(inputType); inputType = nullableType instanceof graphql_1.GraphQLList ? nullableType.ofType : null; break; case 'ObjectValue': const objectType = graphql_1.getNamedType(inputType); objectFieldDefs = objectType instanceof graphql_1.GraphQLInputObjectType ? objectType.getFields() : null; break; case 'ObjectField': const objectField = state.name && objectFieldDefs ? objectFieldDefs[state.name] : null; inputType = objectField && objectField.type; break; case 'NamedType': if (state.name) { type = schema.getType(state.name); } break; } }); return { argDef, argDefs, directiveDef, enumValue, fieldDef, inputType, objectFieldDefs, parentType, type, }; } 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