UNPKG

graphql-language-service-server

Version:
277 lines 12.8 kB
import { GraphQLError, Kind, parse, print, isTypeDefinitionNode, typeFromAST, } from 'graphql'; import { getAutocompleteSuggestions, getHoverInformation, validateQuery, getRange, DIAGNOSTIC_SEVERITY, getOutline, getDefinitionQueryResultForFragmentSpread, getDefinitionQueryResultForDefinitionNode, getDefinitionQueryResultForNamedType, getDefinitionQueryResultForField, getASTNodeAtPosition, getTokenAtPosition, getTypeInfo, getDefinitionQueryResultForArgument, } from 'graphql-language-service'; import { SymbolKind, } from 'vscode-languageserver-types'; const KIND_TO_SYMBOL_KIND = { [Kind.FIELD]: SymbolKind.Field, [Kind.OPERATION_DEFINITION]: SymbolKind.Class, [Kind.FRAGMENT_DEFINITION]: SymbolKind.Class, [Kind.FRAGMENT_SPREAD]: SymbolKind.Struct, [Kind.OBJECT_TYPE_DEFINITION]: SymbolKind.Class, [Kind.ENUM_TYPE_DEFINITION]: SymbolKind.Enum, [Kind.ENUM_VALUE_DEFINITION]: SymbolKind.EnumMember, [Kind.INPUT_OBJECT_TYPE_DEFINITION]: SymbolKind.Class, [Kind.INPUT_VALUE_DEFINITION]: SymbolKind.Field, [Kind.FIELD_DEFINITION]: SymbolKind.Field, [Kind.INTERFACE_TYPE_DEFINITION]: SymbolKind.Interface, [Kind.DOCUMENT]: SymbolKind.File, FieldWithArguments: SymbolKind.Method, }; function getKind(tree) { if (tree.kind === 'FieldDefinition' && tree.children && tree.children.length > 0) { return KIND_TO_SYMBOL_KIND.FieldWithArguments; } return KIND_TO_SYMBOL_KIND[tree.kind]; } export class GraphQLLanguageService { constructor(cache, logger) { this._graphQLCache = cache; this._graphQLConfig = cache.getGraphQLConfig(); this._logger = logger; } getConfigForURI(uri) { const config = this._graphQLCache.getProjectForFile(uri); if (config) { return config; } } async getDiagnostics(document, uri, isRelayCompatMode) { var _a, _b; let documentHasExtensions = false; const projectConfig = this.getConfigForURI(uri); if (!projectConfig || !document || document.trim().length < 2) { return []; } const { schema: schemaPath, name: projectName, extensions } = projectConfig; try { const documentAST = parse(document); if (!schemaPath || uri !== schemaPath) { documentHasExtensions = documentAST.definitions.some(definition => { switch (definition.kind) { case Kind.OBJECT_TYPE_DEFINITION: case Kind.INTERFACE_TYPE_DEFINITION: case Kind.ENUM_TYPE_DEFINITION: case Kind.UNION_TYPE_DEFINITION: case Kind.SCALAR_TYPE_DEFINITION: case Kind.INPUT_OBJECT_TYPE_DEFINITION: case Kind.SCALAR_TYPE_EXTENSION: case Kind.OBJECT_TYPE_EXTENSION: case Kind.INTERFACE_TYPE_EXTENSION: case Kind.UNION_TYPE_EXTENSION: case Kind.ENUM_TYPE_EXTENSION: case Kind.INPUT_OBJECT_TYPE_EXTENSION: case Kind.DIRECTIVE_DEFINITION: return true; } return false; }); } } catch (error) { if (error instanceof GraphQLError) { const range = getRange((_b = (_a = error.locations) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : { column: 0, line: 0 }, document); return [ { severity: DIAGNOSTIC_SEVERITY.Error, message: error.message, source: 'GraphQL: Syntax', range, }, ]; } throw error; } let source = document; const fragmentDefinitions = await this._graphQLCache.getFragmentDefinitions(projectConfig); const fragmentDependencies = await this._graphQLCache.getFragmentDependencies(document, fragmentDefinitions); const dependenciesSource = fragmentDependencies.reduce((prev, cur) => `${prev} ${print(cur.definition)}`, ''); source = `${source} ${dependenciesSource}`; let validationAst = null; try { validationAst = parse(source); } catch (_c) { return []; } let customRules = null; if ((extensions === null || extensions === void 0 ? void 0 : extensions.customValidationRules) && typeof extensions.customValidationRules === 'function') { customRules = extensions.customValidationRules(this._graphQLConfig); } const schema = await this._graphQLCache.getSchema(projectName, documentHasExtensions); if (!schema) { return []; } return validateQuery(validationAst, schema, customRules, isRelayCompatMode); } async getAutocompleteSuggestions(query, position, filePath) { var _a, _b, _c; const projectConfig = this.getConfigForURI(filePath); if (!projectConfig) { return []; } const schema = await this._graphQLCache.getSchema(projectConfig.name); if (!schema) { return []; } let fragmentInfo = []; try { const fragmentDefinitions = await this._graphQLCache.getFragmentDefinitions(projectConfig); fragmentInfo = Array.from(fragmentDefinitions).map(([, info]) => info.definition); } catch (_d) { } return getAutocompleteSuggestions(schema, query, position, undefined, fragmentInfo, { uri: filePath, fillLeafsOnComplete: (_c = (_b = (_a = projectConfig === null || projectConfig === void 0 ? void 0 : projectConfig.extensions) === null || _a === void 0 ? void 0 : _a.languageService) === null || _b === void 0 ? void 0 : _b.fillLeafsOnComplete) !== null && _c !== void 0 ? _c : false, }); } async getHoverInformation(query, position, filePath, options) { const projectConfig = this.getConfigForURI(filePath); if (!projectConfig) { return ''; } const schema = await this._graphQLCache.getSchema(projectConfig.name); if (schema) { return getHoverInformation(schema, query, position, undefined, options); } return ''; } async getDefinition(query, position, filePath) { const projectConfig = this.getConfigForURI(filePath); if (!projectConfig) { return null; } const schema = await this._graphQLCache.getSchema(projectConfig.name); if (!schema) { return null; } let ast; try { ast = parse(query); } catch (_a) { return null; } const node = getASTNodeAtPosition(query, ast, position); const type = node && typeFromAST(schema, node); let queryResult = null; if (node) { switch (node.kind) { case Kind.FRAGMENT_SPREAD: queryResult = await this._getDefinitionForFragmentSpread(query, ast, node, filePath, projectConfig); break; case Kind.FRAGMENT_DEFINITION: case Kind.OPERATION_DEFINITION: queryResult = getDefinitionQueryResultForDefinitionNode(filePath, query, node); break; case Kind.NAMED_TYPE: queryResult = await this._getDefinitionForNamedType(query, ast, node, filePath, projectConfig); break; case Kind.FIELD: queryResult = await this._getDefinitionForField(query, ast, node, filePath, projectConfig, position); break; case Kind.ARGUMENT: queryResult = await this._getDefinitionForArgument(query, ast, node, filePath, projectConfig, position); break; } } if (queryResult) { return { ...queryResult, node, type, }; } return null; } async getDocumentSymbols(document, filePath) { var _a; const outline = await this.getOutline(document); if (!outline) { return []; } const output = []; const input = outline.outlineTrees.map((tree) => [null, tree]); while (input.length > 0) { const res = input.pop(); if (!res) { return []; } const [parent, tree] = res; if (!tree) { return []; } output.push({ name: (_a = tree.representativeName) !== null && _a !== void 0 ? _a : 'Anonymous', kind: getKind(tree), location: { uri: filePath, range: { start: tree.startPosition, end: tree.endPosition, }, }, containerName: parent ? parent.representativeName : undefined, }); input.push(...tree.children.map(child => [tree, child])); } return output; } async _getDefinitionForNamedType(query, ast, node, filePath, projectConfig) { const objectTypeDefinitions = await this._graphQLCache.getObjectTypeDefinitions(projectConfig); const dependencies = await this._graphQLCache.getObjectTypeDependenciesForAST(ast, objectTypeDefinitions); const localOperationDefinitionInfos = ast.definitions .filter(isTypeDefinitionNode) .map((definition) => ({ filePath, content: query, definition, })); return getDefinitionQueryResultForNamedType(query, node, dependencies.concat(localOperationDefinitionInfos)); } async _getDefinitionForField(query, _ast, _node, _filePath, projectConfig, position) { var _a; const token = getTokenAtPosition(query, position); const schema = await this._graphQLCache.getSchema(projectConfig.name); const typeInfo = getTypeInfo(schema, token.state); const fieldName = (_a = typeInfo.fieldDef) === null || _a === void 0 ? void 0 : _a.name; if (typeInfo && fieldName) { const parentTypeName = typeInfo.parentType.toString(); const objectTypeDefinitions = await this._graphQLCache.getObjectTypeDefinitions(projectConfig); const dependencies = [...objectTypeDefinitions.values()]; return getDefinitionQueryResultForField(fieldName, parentTypeName, dependencies); } return null; } async _getDefinitionForArgument(query, _ast, _node, _filePath, projectConfig, position) { var _a, _b, _c, _d; const token = getTokenAtPosition(query, position); const schema = await this._graphQLCache.getSchema(projectConfig.name); const typeInfo = getTypeInfo(schema, token.state); const fieldName = (_a = typeInfo.fieldDef) === null || _a === void 0 ? void 0 : _a.name; const argumentName = (_b = typeInfo.argDef) === null || _b === void 0 ? void 0 : _b.name; if (typeInfo && fieldName && argumentName) { const objectTypeDefinitions = await this._graphQLCache.getObjectTypeDefinitions(projectConfig); const dependencies = [...objectTypeDefinitions.values()]; return getDefinitionQueryResultForArgument(argumentName, fieldName, (_d = (_c = typeInfo.argDef) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.name, dependencies); } return null; } async _getDefinitionForFragmentSpread(query, ast, node, filePath, projectConfig) { const fragmentDefinitions = await this._graphQLCache.getFragmentDefinitions(projectConfig); const dependencies = await this._graphQLCache.getFragmentDependenciesForAST(ast, fragmentDefinitions); const localFragDefinitions = ast.definitions.filter(definition => definition.kind === Kind.FRAGMENT_DEFINITION); const typeCastedDefs = localFragDefinitions; const localFragInfos = typeCastedDefs.map((definition) => ({ filePath, content: query, definition, })); return getDefinitionQueryResultForFragmentSpread(query, node, dependencies.concat(localFragInfos)); } async getOutline(documentText) { return getOutline(documentText); } } //# sourceMappingURL=GraphQLLanguageService.js.map