graphql-language-service-server
Version:
Server process backing the GraphQL Language Service
281 lines • 13.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphQLLanguageService = void 0;
const graphql_1 = require("graphql");
const graphql_language_service_1 = require("graphql-language-service");
const vscode_languageserver_types_1 = require("vscode-languageserver-types");
const KIND_TO_SYMBOL_KIND = {
[graphql_1.Kind.FIELD]: vscode_languageserver_types_1.SymbolKind.Field,
[graphql_1.Kind.OPERATION_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Class,
[graphql_1.Kind.FRAGMENT_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Class,
[graphql_1.Kind.FRAGMENT_SPREAD]: vscode_languageserver_types_1.SymbolKind.Struct,
[graphql_1.Kind.OBJECT_TYPE_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Class,
[graphql_1.Kind.ENUM_TYPE_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Enum,
[graphql_1.Kind.ENUM_VALUE_DEFINITION]: vscode_languageserver_types_1.SymbolKind.EnumMember,
[graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Class,
[graphql_1.Kind.INPUT_VALUE_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Field,
[graphql_1.Kind.FIELD_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Field,
[graphql_1.Kind.INTERFACE_TYPE_DEFINITION]: vscode_languageserver_types_1.SymbolKind.Interface,
[graphql_1.Kind.DOCUMENT]: vscode_languageserver_types_1.SymbolKind.File,
FieldWithArguments: vscode_languageserver_types_1.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];
}
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 = (0, graphql_1.parse)(document);
if (!schemaPath || uri !== schemaPath) {
documentHasExtensions = documentAST.definitions.some(definition => {
switch (definition.kind) {
case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
case graphql_1.Kind.UNION_TYPE_DEFINITION:
case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
case graphql_1.Kind.SCALAR_TYPE_EXTENSION:
case graphql_1.Kind.OBJECT_TYPE_EXTENSION:
case graphql_1.Kind.INTERFACE_TYPE_EXTENSION:
case graphql_1.Kind.UNION_TYPE_EXTENSION:
case graphql_1.Kind.ENUM_TYPE_EXTENSION:
case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION:
case graphql_1.Kind.DIRECTIVE_DEFINITION:
return true;
}
return false;
});
}
}
catch (error) {
if (error instanceof graphql_1.GraphQLError) {
const range = (0, graphql_language_service_1.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: graphql_language_service_1.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} ${(0, graphql_1.print)(cur.definition)}`, '');
source = `${source} ${dependenciesSource}`;
let validationAst = null;
try {
validationAst = (0, graphql_1.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 (0, graphql_language_service_1.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 (0, graphql_language_service_1.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 (0, graphql_language_service_1.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 = (0, graphql_1.parse)(query);
}
catch (_a) {
return null;
}
const node = (0, graphql_language_service_1.getASTNodeAtPosition)(query, ast, position);
const type = node && (0, graphql_1.typeFromAST)(schema, node);
let queryResult = null;
if (node) {
switch (node.kind) {
case graphql_1.Kind.FRAGMENT_SPREAD:
queryResult = await this._getDefinitionForFragmentSpread(query, ast, node, filePath, projectConfig);
break;
case graphql_1.Kind.FRAGMENT_DEFINITION:
case graphql_1.Kind.OPERATION_DEFINITION:
queryResult = (0, graphql_language_service_1.getDefinitionQueryResultForDefinitionNode)(filePath, query, node);
break;
case graphql_1.Kind.NAMED_TYPE:
queryResult = await this._getDefinitionForNamedType(query, ast, node, filePath, projectConfig);
break;
case graphql_1.Kind.FIELD:
queryResult = await this._getDefinitionForField(query, ast, node, filePath, projectConfig, position);
break;
case graphql_1.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(graphql_1.isTypeDefinitionNode)
.map((definition) => ({
filePath,
content: query,
definition,
}));
return (0, graphql_language_service_1.getDefinitionQueryResultForNamedType)(query, node, dependencies.concat(localOperationDefinitionInfos));
}
async _getDefinitionForField(query, _ast, _node, _filePath, projectConfig, position) {
var _a;
const token = (0, graphql_language_service_1.getTokenAtPosition)(query, position);
const schema = await this._graphQLCache.getSchema(projectConfig.name);
const typeInfo = (0, graphql_language_service_1.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 (0, graphql_language_service_1.getDefinitionQueryResultForField)(fieldName, parentTypeName, dependencies);
}
return null;
}
async _getDefinitionForArgument(query, _ast, _node, _filePath, projectConfig, position) {
var _a, _b, _c, _d;
const token = (0, graphql_language_service_1.getTokenAtPosition)(query, position);
const schema = await this._graphQLCache.getSchema(projectConfig.name);
const typeInfo = (0, graphql_language_service_1.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 (0, graphql_language_service_1.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 === graphql_1.Kind.FRAGMENT_DEFINITION);
const typeCastedDefs = localFragDefinitions;
const localFragInfos = typeCastedDefs.map((definition) => ({
filePath,
content: query,
definition,
}));
return (0, graphql_language_service_1.getDefinitionQueryResultForFragmentSpread)(query, node, dependencies.concat(localFragInfos));
}
async getOutline(documentText) {
return (0, graphql_language_service_1.getOutline)(documentText);
}
}
exports.GraphQLLanguageService = GraphQLLanguageService;
//# sourceMappingURL=GraphQLLanguageService.js.map