UNPKG

graphql-language-service-server

Version:
888 lines 41.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.processDiagnosticsMessage = exports.MessageProcessor = void 0; const node_fs_1 = require("node:fs"); const promises_1 = require("node:fs/promises"); const path = __importStar(require("node:path")); const vscode_uri_1 = require("vscode-uri"); const graphql_language_service_1 = require("graphql-language-service"); const GraphQLLanguageService_1 = require("./GraphQLLanguageService"); const GraphQLCache_1 = require("./GraphQLCache"); const parseDocument_1 = require("./parseDocument"); const graphql_1 = require("graphql"); const node_os_1 = require("node:os"); const graphql_config_1 = require("graphql-config"); const constants_1 = require("./constants"); const fast_glob_1 = __importDefault(require("fast-glob")); const common_1 = require("./common"); const debounce_promise_1 = __importDefault(require("debounce-promise")); const configDocLink = 'https://www.npmjs.com/package/graphql-language-service-server#user-content-graphql-configuration-file'; function toPosition(position) { return new graphql_language_service_1.Position(position.line, position.character); } class MessageProcessor { constructor({ logger, fileExtensions, graphqlFileExtensions, loadConfigOptions, config, parser, tmpDir, connection, }) { this._textDocumentCache = new Map(); this._isInitialized = false; this._isGraphQLConfigMissing = null; this._willShutdown = false; this._rootPath = process.cwd(); if (config) { this._providedConfig = config; } this._connection = connection; this._logger = logger; this._parser = async (text, uri) => { const p = parser !== null && parser !== void 0 ? parser : parseDocument_1.parseDocument; return p(text, uri, fileExtensions, graphqlFileExtensions, this._logger); }; this._tmpDir = tmpDir || (0, node_os_1.tmpdir)(); this._tmpDirBase = path.join(this._tmpDir, 'graphql-language-service'); this._loadConfigOptions = { legacy: true, ...loadConfigOptions }; if (!(0, node_fs_1.existsSync)(this._tmpDirBase)) { void (0, node_fs_1.mkdirSync)(this._tmpDirBase); } } get connection() { return this._connection; } set connection(connection) { this._connection = connection; } async handleInitializeRequest(params, _token, configDir) { if (!params) { throw new Error('`params` argument is required to initialize.'); } const serverCapabilities = { capabilities: { workspaceSymbolProvider: true, documentSymbolProvider: true, completionProvider: { resolveProvider: true, triggerCharacters: [' ', ':', '$', '(', '@', '\n'], }, definitionProvider: true, textDocumentSync: 1, hoverProvider: true, workspace: { workspaceFolders: { supported: true, changeNotifications: true, }, }, }, }; this._rootPath = configDir ? configDir.trim() : params.rootUri || this._rootPath; if (!this._rootPath) { this._logger.warn('no rootPath configured in extension or server, defaulting to cwd'); } this._logger.info(JSON.stringify({ type: 'usage', messageType: 'initialize', })); return serverCapabilities; } async _initializeGraphQLCaches() { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; const settings = await this._connection.workspace.getConfiguration({ section: 'graphql-config', }); const vscodeSettings = await this._connection.workspace.getConfiguration({ section: 'vscode-graphql', }); this._settings = { ...settings, ...vscodeSettings }; const rootDir = ((_b = (_a = this._settings) === null || _a === void 0 ? void 0 : _a.load) === null || _b === void 0 ? void 0 : _b.rootDir.length) ? (_d = (_c = this._settings) === null || _c === void 0 ? void 0 : _c.load) === null || _d === void 0 ? void 0 : _d.rootDir : this._rootPath; if (settings === null || settings === void 0 ? void 0 : settings.dotEnvPath) { require('dotenv').config({ path: path.resolve(rootDir, settings.dotEnvPath), }); } this._rootPath = rootDir; this._loadConfigOptions = { ...Object.keys((_f = (_e = this._settings) === null || _e === void 0 ? void 0 : _e.load) !== null && _f !== void 0 ? _f : {}).reduce((agg, key) => { var _a; const value = (_a = this._settings) === null || _a === void 0 ? void 0 : _a.load[key]; if (value === undefined || value === null) { delete agg[key]; } return agg; }, (_g = this._settings.load) !== null && _g !== void 0 ? _g : {}), rootDir, }; const onSchemaChange = (0, debounce_promise_1.default)(async (project) => { const { cacheSchemaFileForLookup } = this.getCachedSchemaSettings(project); if (!cacheSchemaFileForLookup) { return; } const unwrappedSchema = (0, common_1.unwrapProjectSchema)(project); const sdlOnly = (0, common_1.isProjectSDLOnly)(unwrappedSchema); if (sdlOnly) { return; } return this.cacheConfigSchemaFile(project); }, 400); try { this._logger.level = ((_h = this._settings) === null || _h === void 0 ? void 0 : _h.debug) === true ? 1 : 0; if (this._providedConfig) { this._graphQLCache = new GraphQLCache_1.GraphQLCache({ config: this._providedConfig, logger: this._logger, parser: this._parser, configDir: rootDir, onSchemaChange, schemaCacheTTL: (_j = this._settings) === null || _j === void 0 ? void 0 : _j.schemaCacheTTL, }); this._languageService = new GraphQLLanguageService_1.GraphQLLanguageService(this._graphQLCache, this._logger); } else { this._graphQLCache = await (0, GraphQLCache_1.getGraphQLCache)({ parser: this._parser, loadConfigOptions: this._loadConfigOptions, logger: this._logger, onSchemaChange, schemaCacheTTL: (_k = this._settings) === null || _k === void 0 ? void 0 : _k.schemaCacheTTL, }); this._languageService = new GraphQLLanguageService_1.GraphQLLanguageService(this._graphQLCache, this._logger); } const config = this._graphQLCache.getGraphQLConfig(); if (config) { await this._cacheAllProjectFiles(config); this._isInitialized = true; this._isGraphQLConfigMissing = false; this._logger.info('GraphQL Language Server caches initialized'); } } catch (err) { this._handleConfigError({ err }); } } _handleConfigError({ err }) { var _a; if (err instanceof graphql_config_1.ConfigNotFoundError || err instanceof graphql_config_1.ConfigEmptyError) { this._isGraphQLConfigMissing = true; this._logConfigError(err.message); } else if (err instanceof graphql_config_1.ProjectNotFoundError) { this._logConfigError('Project not found for this file - make sure that a schema is present in the config file or for the project'); } else if (err instanceof graphql_config_1.ConfigInvalidError) { this._isGraphQLConfigMissing = true; this._logConfigError(`Invalid configuration\n${err.message}`); } else if (err instanceof graphql_config_1.LoaderNoResultError) { this._isGraphQLConfigMissing = true; this._logConfigError(err.message); return; } else { this._isGraphQLConfigMissing = true; this._logConfigError((_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err === null || err === void 0 ? void 0 : err.toString()); } } _logConfigError(errorMessage) { this._logger.error('WARNING: graphql-config error, only highlighting is enabled:\n' + errorMessage + `\nfor more information on using 'graphql-config' with 'graphql-language-service-server', \nsee the documentation at ${configDocLink}`); } async _isGraphQLConfigFile(uri) { var _a, _b, _c, _d, _e; const configMatchers = ['graphql.config', 'graphqlrc', 'graphqlconfig']; if ((_c = (_b = (_a = this._settings) === null || _a === void 0 ? void 0 : _a.load) === null || _b === void 0 ? void 0 : _b.fileName) === null || _c === void 0 ? void 0 : _c.length) { configMatchers.push(this._settings.load.fileName); } const fileMatch = configMatchers .filter(Boolean) .some(v => { var _a; return (_a = uri.match(v)) === null || _a === void 0 ? void 0 : _a.length; }); if (fileMatch) { return fileMatch; } if ((_d = uri.match('package.json')) === null || _d === void 0 ? void 0 : _d.length) { try { const pkgConfig = await (0, promises_1.readFile)(vscode_uri_1.URI.parse(uri).fsPath, 'utf-8'); return Boolean((_e = JSON.parse(pkgConfig)) === null || _e === void 0 ? void 0 : _e.graphql); } catch (_f) { } } return false; } async _loadConfigOrSkip(uri) { try { const isGraphQLConfigFile = await this._isGraphQLConfigFile(uri); if (!this._isInitialized) { if (this._isGraphQLConfigMissing === true && !isGraphQLConfigFile) { return true; } await this._initializeGraphQLCaches(); return isGraphQLConfigFile; } if (isGraphQLConfigFile) { await this._initializeGraphQLCaches(); } return isGraphQLConfigFile; } catch (err) { this._logger.error(String(err)); return true; } } async handleDidOpenOrSaveNotification(params) { var _a, _b; const { textDocument } = params; const { uri } = textDocument; const shouldSkip = await this._loadConfigOrSkip(uri); if (shouldSkip) { return { uri, diagnostics: [] }; } if (!textDocument) { throw new Error('`textDocument` argument is required.'); } const diagnostics = []; if (!this._isInitialized) { return { uri, diagnostics }; } try { const project = this._graphQLCache.getProjectForFile(uri); if (project) { const text = 'text' in textDocument && textDocument.text; const { contents } = await this._parseAndCacheFile(uri, project, text); if (((_b = (_a = project === null || project === void 0 ? void 0 : project.extensions) === null || _a === void 0 ? void 0 : _a.languageService) === null || _b === void 0 ? void 0 : _b.enableValidation) !== false) { await Promise.all(contents.map(async ({ query, range }) => { const results = await this._languageService.getDiagnostics(query, uri, this._isRelayCompatMode(query)); if (results && results.length > 0) { diagnostics.push(...processDiagnosticsMessage(results, query, range)); } })); } } this._logger.log(JSON.stringify({ type: 'usage', messageType: 'textDocument/didOpenOrSave', projectName: project === null || project === void 0 ? void 0 : project.name, fileName: uri, })); return { uri, diagnostics }; } catch (err) { this._handleConfigError({ err, uri }); return { uri, diagnostics }; } } async handleDidChangeNotification(params) { var _a, _b, _c; if (this._isGraphQLConfigMissing || !this._isInitialized || !this._graphQLCache) { return null; } if (!((_a = params === null || params === void 0 ? void 0 : params.textDocument) === null || _a === void 0 ? void 0 : _a.uri) || !params.contentChanges) { throw new Error('`textDocument.uri` and `contentChanges` arguments are required.'); } const { textDocument, contentChanges } = params; const { uri } = textDocument; try { const project = this._graphQLCache.getProjectForFile(uri); if (!project) { return { uri, diagnostics: [] }; } const { contents } = await this._parseAndCacheFile(uri, project, contentChanges.at(-1).text); const diagnostics = []; if (((_c = (_b = project === null || project === void 0 ? void 0 : project.extensions) === null || _b === void 0 ? void 0 : _b.languageService) === null || _c === void 0 ? void 0 : _c.enableValidation) !== false) { try { await Promise.all(contents.map(async ({ query, range }) => { const results = await this._languageService.getDiagnostics(query, uri, this._isRelayCompatMode(query)); if (results && results.length > 0) { diagnostics.push(...processDiagnosticsMessage(results, query, range)); } })); } catch (_d) { } } this._logger.log(JSON.stringify({ type: 'usage', messageType: 'textDocument/didChange', projectName: project === null || project === void 0 ? void 0 : project.name, fileName: uri, })); return { uri, diagnostics }; } catch (err) { this._handleConfigError({ err, uri }); return { uri, diagnostics: [] }; } } async handleDidChangeConfiguration(_params) { await this._initializeGraphQLCaches(); this._logger.log(JSON.stringify({ type: 'usage', messageType: 'workspace/didChangeConfiguration', })); return {}; } handleDidCloseNotification(params) { if (!this._isInitialized) { return; } if (!(params === null || params === void 0 ? void 0 : params.textDocument)) { throw new Error('`textDocument` is required.'); } const { textDocument } = params; const { uri } = textDocument; if (this._textDocumentCache.has(uri)) { this._textDocumentCache.delete(uri); } const project = this._graphQLCache.getProjectForFile(uri); this._logger.log(JSON.stringify({ type: 'usage', messageType: 'textDocument/didClose', projectName: project === null || project === void 0 ? void 0 : project.name, fileName: uri, })); } handleShutdownRequest() { this._willShutdown = true; } handleExitNotification() { process.exit(this._willShutdown ? 0 : 1); } validateDocumentAndPosition(params) { var _a; if (!((_a = params === null || params === void 0 ? void 0 : params.textDocument) === null || _a === void 0 ? void 0 : _a.uri) || !params.position) { throw new Error('`textDocument.uri` and `position` arguments are required.'); } } async handleCompletionRequest(params) { if (!this._isInitialized) { return { items: [], isIncomplete: false }; } this.validateDocumentAndPosition(params); const { textDocument, position } = params; const cachedDocument = this._getCachedDocument(textDocument.uri); if (!cachedDocument) { return { items: [], isIncomplete: false }; } const found = cachedDocument.contents.find(content => { const currentRange = content.range; if (currentRange === null || currentRange === void 0 ? void 0 : currentRange.containsPosition(toPosition(position))) { return true; } }); if (!found) { return { items: [], isIncomplete: false }; } const { query, range } = found; if (range) { position.line -= range.start.line; } const result = await this._languageService.getAutocompleteSuggestions(query, toPosition(position), textDocument.uri); const project = this._graphQLCache.getProjectForFile(textDocument.uri); this._logger.log(JSON.stringify({ type: 'usage', messageType: 'textDocument/completion', projectName: project === null || project === void 0 ? void 0 : project.name, fileName: textDocument.uri, })); return { items: result, isIncomplete: false }; } async handleHoverRequest(params) { if (!this._isInitialized) { return { contents: [] }; } this.validateDocumentAndPosition(params); const { textDocument, position } = params; const cachedDocument = this._getCachedDocument(textDocument.uri); if (!cachedDocument) { return { contents: [] }; } const found = cachedDocument.contents.find(content => { const currentRange = content.range; if (currentRange === null || currentRange === void 0 ? void 0 : currentRange.containsPosition(toPosition(position))) { return true; } }); if (!found) { return { contents: [] }; } const { query, range } = found; if (range) { position.line -= range.start.line; } const result = await this._languageService.getHoverInformation(query, toPosition(position), textDocument.uri, { useMarkdown: true }); return { contents: result, }; } async _parseAndCacheFile(uri, project, text) { try { const fileText = text || (await (0, promises_1.readFile)(vscode_uri_1.URI.parse(uri).fsPath, 'utf-8')); const contents = await this._parser(fileText, uri); const cachedDocument = this._textDocumentCache.get(uri); const version = cachedDocument ? cachedDocument.version++ : 0; await this._invalidateCache({ uri, version }, uri, contents); await this._updateFragmentDefinition(uri, contents); await this._updateObjectTypeDefinition(uri, contents, project); await this._updateSchemaIfChanged(project, uri); return { contents, version }; } catch (_a) { return { contents: [], version: 0 }; } } async handleWatchedFilesChangedNotification(params) { const resultsForChanges = Promise.all(params.changes.map(async (change) => { var _a, _b; const shouldSkip = await this._loadConfigOrSkip(change.uri); if (shouldSkip) { return { uri: change.uri, diagnostics: [] }; } if (change.type === graphql_language_service_1.FileChangeTypeKind.Created || change.type === graphql_language_service_1.FileChangeTypeKind.Changed) { const { uri } = change; try { let diagnostics = []; const project = this._graphQLCache.getProjectForFile(uri); if (project) { const { contents } = await this._parseAndCacheFile(uri, project); if (((_b = (_a = project === null || project === void 0 ? void 0 : project.extensions) === null || _a === void 0 ? void 0 : _a.languageService) === null || _b === void 0 ? void 0 : _b.enableValidation) !== false) { diagnostics = (await Promise.all(contents.map(async ({ query, range }) => { const results = await this._languageService.getDiagnostics(query, uri, this._isRelayCompatMode(query)); if (results && results.length > 0) { return processDiagnosticsMessage(results, query, range); } return []; }))).reduce((left, right) => left.concat(right), diagnostics); } return { uri, diagnostics }; } } catch (_c) { } return { uri, diagnostics: [] }; } if (change.type === graphql_language_service_1.FileChangeTypeKind.Deleted) { await this._updateFragmentDefinition(change.uri, []); await this._updateObjectTypeDefinition(change.uri, []); } })); this._logger.log(JSON.stringify({ type: 'usage', messageType: 'workspace/didChangeWatchedFiles', files: params.changes.map(change => change.uri), })); return resultsForChanges; } async handleDefinitionRequest(params, _token) { var _a, _b; if (!this._isInitialized) { return []; } if (!(params === null || params === void 0 ? void 0 : params.textDocument) || !params.position) { throw new Error('`textDocument` and `position` arguments are required.'); } const { textDocument, position } = params; const project = this._graphQLCache.getProjectForFile(textDocument.uri); const cachedDocument = this._getCachedDocument(textDocument.uri); if (!cachedDocument || !project) { return []; } const found = cachedDocument.contents.find(content => { const currentRange = content.range; if (currentRange === null || currentRange === void 0 ? void 0 : currentRange.containsPosition(toPosition(position))) { return true; } }); if (!found) { return []; } const { query, range: parentRange } = found; if (parentRange) { position.line -= parentRange.start.line; } let result = null; try { result = await this._languageService.getDefinition(query, toPosition(position), textDocument.uri); } catch (_c) { } const inlineFragments = []; try { (0, graphql_1.visit)((0, graphql_1.parse)(query), { FragmentDefinition(node) { inlineFragments.push(node.name.value); }, }); } catch (_d) { } const locateCommand = (_b = (_a = project === null || project === void 0 ? void 0 : project.extensions) === null || _a === void 0 ? void 0 : _a.languageService) === null || _b === void 0 ? void 0 : _b.locateCommand; const formatted = result ? result.definitions.map(res => { var _a, _b; const defRange = res.range; if (parentRange && res.name) { const isInline = inlineFragments.includes(res.name); const isEmbedded = constants_1.DEFAULT_SUPPORTED_EXTENSIONS.includes(path.extname(res.path)); if (isEmbedded || isInline) { const cachedDoc = this._getCachedDocument(vscode_uri_1.URI.parse(res.path).toString()); const vOffset = isEmbedded ? (_b = (_a = cachedDoc === null || cachedDoc === void 0 ? void 0 : cachedDoc.contents[0].range) === null || _a === void 0 ? void 0 : _a.start.line) !== null && _b !== void 0 ? _b : 0 : parentRange.start.line; defRange.setStart((defRange.start.line += vOffset), defRange.start.character); defRange.setEnd((defRange.end.line += vOffset), defRange.end.character); } } if (locateCommand && result && (result === null || result === void 0 ? void 0 : result.printedName)) { const locateResult = this._getCustomLocateResult(project, result, locateCommand); if (locateResult) { return locateResult; } } return { uri: res.path, range: defRange, }; }) : []; this._logger.log(JSON.stringify({ type: 'usage', messageType: 'textDocument/definition', projectName: project === null || project === void 0 ? void 0 : project.name, fileName: textDocument.uri, })); return formatted; } _getCustomLocateResult(project, result, locateCommand) { if (!result.printedName) { return null; } try { const locateResult = locateCommand(project.name, result.printedName, { node: result.node, type: result.type, project, }); if (typeof locateResult === 'string') { const [uri, startLine = '1', endLine = '1'] = locateResult.split(':'); return { uri, range: new graphql_language_service_1.Range(new graphql_language_service_1.Position(parseInt(startLine, 10), 0), new graphql_language_service_1.Position(parseInt(endLine, 10), 0)), }; } return locateResult; } catch (error) { this._logger.error('There was an error executing user defined locateCommand\n\n' + error.toString()); return null; } } async handleDocumentSymbolRequest(params) { if (!this._isInitialized) { return []; } if (!(params === null || params === void 0 ? void 0 : params.textDocument)) { throw new Error('`textDocument` argument is required.'); } const { textDocument } = params; const cachedDocument = this._getCachedDocument(textDocument.uri); if (!(cachedDocument === null || cachedDocument === void 0 ? void 0 : cachedDocument.contents[0])) { return []; } if (this._settings.largeFileThreshold !== undefined && this._settings.largeFileThreshold < cachedDocument.contents[0].query.length) { return []; } this._logger.log(JSON.stringify({ type: 'usage', messageType: 'textDocument/documentSymbol', fileName: textDocument.uri, })); return this._languageService.getDocumentSymbols(cachedDocument.contents[0].query, textDocument.uri); } async handleWorkspaceSymbolRequest(params) { if (!this._isInitialized) { return []; } if (params.query !== '') { const documents = this._getTextDocuments(); const symbols = []; await Promise.all(documents.map(async ([uri]) => { const cachedDocument = this._getCachedDocument(uri); if (!cachedDocument) { return []; } const docSymbols = await this._languageService.getDocumentSymbols(cachedDocument.contents[0].query, uri); symbols.push(...docSymbols); })); return symbols.filter(symbol => { var _a; return (_a = symbol === null || symbol === void 0 ? void 0 : symbol.name) === null || _a === void 0 ? void 0 : _a.includes(params.query); }); } return []; } _getTextDocuments() { return Array.from(this._textDocumentCache); } async _cacheSchemaText(uri, text, version, project) { try { const contents = await this._parser(text, uri); if (contents.length > 0) { await this._invalidateCache({ version, uri }, uri, contents); await this._updateObjectTypeDefinition(uri, contents, project); } } catch (err) { this._logger.error(String(err)); } } async _cacheSchemaFile(fileUri, project) { try { const matches = await (0, fast_glob_1.default)(fileUri, { cwd: project.dirpath, absolute: true, }); const uri = matches[0]; let version = 1; if (uri) { const schemaUri = vscode_uri_1.URI.file(uri).toString(); const schemaDocument = this._getCachedDocument(schemaUri); if (schemaDocument) { version = schemaDocument.version++; } const schemaText = await (0, promises_1.readFile)(uri, 'utf-8'); await this._cacheSchemaText(schemaUri, schemaText, version); } } catch (err) { this._logger.error(String(err)); } } _getTmpProjectPath(project, prependWithProtocol = true, appendPath) { const baseDir = this._graphQLCache.getGraphQLConfig().dirpath; const workspaceName = path.basename(baseDir); const basePath = path.join(this._tmpDirBase, workspaceName); let projectTmpPath = path.join(basePath, 'projects', project.name); if (!(0, node_fs_1.existsSync)(projectTmpPath)) { (0, node_fs_1.mkdirSync)(projectTmpPath, { recursive: true, }); } if (appendPath) { projectTmpPath = path.join(projectTmpPath, appendPath); } if (prependWithProtocol) { return vscode_uri_1.URI.file(path.resolve(projectTmpPath)).toString(); } return path.resolve(projectTmpPath); } getCachedSchemaSettings(project) { var _a, _b, _c; const config = (_a = project === null || project === void 0 ? void 0 : project.extensions) === null || _a === void 0 ? void 0 : _a.languageService; let cacheSchemaFileForLookup = true; let schemaCacheTTL = 1000 * 30; if ((config === null || config === void 0 ? void 0 : config.cacheSchemaFileForLookup) === false || ((_b = this === null || this === void 0 ? void 0 : this._settings) === null || _b === void 0 ? void 0 : _b.cacheSchemaFileForLookup) === false) { cacheSchemaFileForLookup = false; } if (config === null || config === void 0 ? void 0 : config.schemaCacheTTL) { schemaCacheTTL = config.schemaCacheTTL; } if ((_c = this === null || this === void 0 ? void 0 : this._settings) === null || _c === void 0 ? void 0 : _c.schemaCacheTTL) { schemaCacheTTL = this._settings.schemaCacheTTL; } return { cacheSchemaFileForLookup, schemaCacheTTL }; } async _cacheSchemaFilesForProject(project) { const { cacheSchemaFileForLookup } = this.getCachedSchemaSettings(project); const unwrappedSchema = (0, common_1.unwrapProjectSchema)(project); const sdlOnly = (0, common_1.isProjectSDLOnly)(unwrappedSchema); try { } catch (_a) { } if (cacheSchemaFileForLookup && !sdlOnly) { await this.cacheConfigSchemaFile(project); } else if (sdlOnly) { await Promise.all(unwrappedSchema.map(async (schemaEntry) => this._cacheSchemaFile(schemaEntry, project))); } } async cacheConfigSchemaFile(project) { try { const schema = await this._graphQLCache.getSchema(project.name); if (schema) { let schemaText = (0, graphql_1.printSchema)(schema); const uri = this._getTmpProjectPath(project, true, 'generated-schema.graphql'); const fsPath = this._getTmpProjectPath(project, false, 'generated-schema.graphql'); schemaText = `# This is an automatically generated representation of your schema.\n# Any changes to this file will be overwritten and will not be\n# reflected in the resulting GraphQL schema\n\n${schemaText}`; const cachedSchemaDoc = this._getCachedDocument(uri); this._graphQLCache._schemaMap.set(project.name, schema); try { await (0, promises_1.mkdir)(path.dirname(fsPath), { recursive: true }); } catch (_a) { } if (!cachedSchemaDoc) { await (0, promises_1.writeFile)(fsPath, schemaText, { encoding: 'utf-8', }); await this._cacheSchemaText(uri, schemaText, 0, project); } if (cachedSchemaDoc) { await (0, promises_1.writeFile)(fsPath, schemaText, 'utf-8'); await this._cacheSchemaText(uri, schemaText, cachedSchemaDoc.version++, project); } } } catch (err) { this._logger.error(String(err)); } } async _cacheDocumentFilesforProject(project) { try { const documents = await project.getDocuments(); const documentLocations = new Set(documents .filter(doc => doc.location && doc.rawSDL) .map(doc => doc.location)); return Promise.all(Array.from(documentLocations).map(async (loc) => { var _a; let filePath = loc; if (!path.isAbsolute(filePath)) { filePath = path.join(project.dirpath, loc); } const uri = vscode_uri_1.URI.file(filePath).toString(); const fileContent = await (0, promises_1.readFile)(filePath, 'utf-8'); const contents = await this._parser(fileContent, uri); if (!((_a = contents[0]) === null || _a === void 0 ? void 0 : _a.query)) { return; } await this._updateObjectTypeDefinition(uri, contents); await this._updateFragmentDefinition(uri, contents); await this._invalidateCache({ version: 1, uri }, uri, contents); })); } catch (err) { this._logger.error(`invalid/unknown file in graphql config documents entry:\n '${project.documents}'`); this._logger.error(String(err)); } } async _cacheAllProjectFiles(config) { if (config === null || config === void 0 ? void 0 : config.projects) { return Promise.all(Object.keys(config.projects).map(async (projectName) => { var _a; const project = config.getProject(projectName); await this._cacheSchemaFilesForProject(project); if ((_a = project.documents) === null || _a === void 0 ? void 0 : _a.length) { await this._cacheDocumentFilesforProject(project); } else { this._logger.warn([ `No 'documents' config found for project: ${projectName}.`, 'Fragments and query documents cannot be detected.', 'LSP server will only perform some partial validation and SDL features.', ].join('\n')); } })); } } _isRelayCompatMode(query) { return (query.includes('RelayCompat') || query.includes('react-relay/compat')); } async _updateFragmentDefinition(uri, contents) { const project = this._graphQLCache.getProjectForFile(uri); if (project) { const cacheKey = this._graphQLCache._cacheKeyForProject(project); await this._graphQLCache.updateFragmentDefinition(cacheKey, uri, contents); } } async _updateSchemaIfChanged(project, uri) { await Promise.all((0, common_1.unwrapProjectSchema)(project).map(async (schema) => { const schemaFilePath = path.resolve(project.dirpath, schema); const uriFilePath = vscode_uri_1.URI.parse(uri).fsPath; if (uriFilePath === schemaFilePath) { try { const file = await (0, promises_1.readFile)(schemaFilePath, 'utf-8'); (0, graphql_1.parse)(file, { noLocation: true }); this._graphQLCache.invalidateSchemaCacheForProject(project); } catch (_a) { } } })); } async _updateObjectTypeDefinition(uri, contents, project) { const resolvedProject = project !== null && project !== void 0 ? project : (await this._graphQLCache.getProjectForFile(uri)); if (resolvedProject) { const cacheKey = this._graphQLCache._cacheKeyForProject(resolvedProject); await this._graphQLCache.updateObjectTypeDefinition(cacheKey, uri, contents); } } _getCachedDocument(uri) { if (this._textDocumentCache.has(uri)) { const cachedDocument = this._textDocumentCache.get(uri); if (cachedDocument) { return cachedDocument; } } return null; } async _invalidateCache(textDocument, uri, contents) { var _a; if (this._textDocumentCache.has(uri)) { const cachedDocument = this._textDocumentCache.get(uri); if (cachedDocument && (textDocument === null || textDocument === void 0 ? void 0 : textDocument.version) && cachedDocument.version < textDocument.version) { return this._textDocumentCache.set(uri, { version: textDocument.version, contents, }); } } return this._textDocumentCache.set(uri, { version: (_a = textDocument.version) !== null && _a !== void 0 ? _a : 0, contents, }); } } exports.MessageProcessor = MessageProcessor; function processDiagnosticsMessage(results, query, range) { const queryLines = query.split('\n'); const totalLines = queryLines.length; const lastLineLength = queryLines[totalLines - 1].length; const lastCharacterPosition = new graphql_language_service_1.Position(totalLines, lastLineLength); const processedResults = results.filter(diagnostic => diagnostic.range.end.lessThanOrEqualTo(lastCharacterPosition)); if (range) { const offset = range.start; return processedResults.map(diagnostic => ({ ...diagnostic, range: new graphql_language_service_1.Range(new graphql_language_service_1.Position(diagnostic.range.start.line + offset.line, diagnostic.range.start.character), new graphql_language_service_1.Position(diagnostic.range.end.line + offset.line, diagnostic.range.end.character)), })); } return processedResults; } exports.processDiagnosticsMessage = processDiagnosticsMessage; //# sourceMappingURL=MessageProcessor.js.map