apollo-language-server
Version: 
A language server for Apollo GraphQL projects
172 lines • 6.71 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphQLWorkspace = void 0;
const path_1 = require("path");
const glob_1 = __importDefault(require("glob"));
const config_1 = require("./config");
const client_1 = require("./project/client");
const service_1 = require("./project/service");
const vscode_uri_1 = __importDefault(require("vscode-uri"));
const utilities_1 = require("./utilities");
class GraphQLWorkspace {
    constructor(LanguageServerLoadingHandler, config) {
        this.LanguageServerLoadingHandler = LanguageServerLoadingHandler;
        this.config = config;
        this._projectForFileCache = new Map();
        this.projectsByFolderUri = new Map();
    }
    onDiagnostics(handler) {
        this._onDiagnostics = handler;
    }
    onDecorations(handler) {
        this._onDecorations = handler;
    }
    onSchemaTags(handler) {
        this._onSchemaTags = handler;
    }
    onConfigFilesFound(handler) {
        this._onConfigFilesFound = handler;
    }
    createProject({ config, folder, }) {
        const { clientIdentity } = this.config;
        const project = (0, config_1.isClientConfig)(config)
            ? new client_1.GraphQLClientProject({
                config,
                loadingHandler: this.LanguageServerLoadingHandler,
                rootURI: vscode_uri_1.default.parse(folder.uri),
                clientIdentity,
            })
            : new service_1.GraphQLServiceProject({
                config: config,
                loadingHandler: this.LanguageServerLoadingHandler,
                rootURI: vscode_uri_1.default.parse(folder.uri),
                clientIdentity,
            });
        project.onDiagnostics((params) => {
            this._onDiagnostics && this._onDiagnostics(params);
        });
        if ((0, client_1.isClientProject)(project)) {
            project.onDecorations((params) => {
                this._onDecorations && this._onDecorations(params);
            });
            project.onSchemaTags((tags) => {
                this._onSchemaTags && this._onSchemaTags(tags);
            });
        }
        project.whenReady.then(() => project.validate());
        return project;
    }
    async addProjectsInFolder(folder) {
        const apolloConfigFiles = glob_1.default.sync("**/apollo.config.@(js|ts|cjs)", {
            cwd: vscode_uri_1.default.parse(folder.uri).fsPath,
            absolute: true,
            ignore: "**/node_modules/**",
        });
        const apolloConfigFolders = new Set(apolloConfigFiles.map(path_1.dirname));
        let foundConfigs = [];
        const projectConfigs = Array.from(apolloConfigFolders).map((configFolder) => (0, config_1.loadConfig)({ configPath: configFolder, requireConfig: true })
            .then((config) => {
            if (config) {
                foundConfigs.push(config);
                const projectsForConfig = config.projects.map((projectConfig) => this.createProject({ config, folder }));
                const existingProjects = this.projectsByFolderUri.get(folder.uri) || [];
                this.projectsByFolderUri.set(folder.uri, [
                    ...existingProjects,
                    ...projectsForConfig,
                ]);
            }
            else {
                utilities_1.Debug.error(`Workspace failed to load config from: ${configFolder}/`);
            }
        })
            .catch((error) => utilities_1.Debug.error(error)));
        await Promise.all(projectConfigs);
        if (this._onConfigFilesFound) {
            this._onConfigFilesFound(foundConfigs);
        }
    }
    reloadService() {
        this._projectForFileCache.clear();
        this.projectsByFolderUri.forEach((projects, uri) => {
            this.projectsByFolderUri.set(uri, projects.map((project) => {
                project.clearAllDiagnostics();
                return this.createProject({
                    config: project.config,
                    folder: { uri },
                });
            }));
        });
    }
    async reloadProjectForConfig(configUri) {
        const configPath = (0, path_1.dirname)(vscode_uri_1.default.parse(configUri).fsPath);
        let config, error;
        try {
            config = await (0, config_1.loadConfig)({ configPath, requireConfig: true });
        }
        catch (e) {
            error = e;
        }
        const project = this.projectForFile(configUri);
        if (!config && this._onConfigFilesFound) {
            this._onConfigFilesFound(error);
        }
        if (project && config) {
            await Promise.all(project.updateConfig(config));
            this.reloadService();
        }
        if (!project && config) {
            const folderUri = vscode_uri_1.default.file(configPath).toString();
            const newProject = this.createProject({
                config,
                folder: { uri: folderUri },
            });
            const existingProjects = this.projectsByFolderUri.get(folderUri) || [];
            this.projectsByFolderUri.set(folderUri, [
                ...existingProjects,
                newProject,
            ]);
            this.reloadService();
        }
    }
    updateSchemaTag(selection) {
        const serviceID = selection.detail;
        if (!serviceID)
            return;
        this.projectsByFolderUri.forEach((projects) => {
            projects.forEach((project) => {
                if ((0, client_1.isClientProject)(project) && project.serviceID === serviceID) {
                    project.updateSchemaTag(selection.label);
                }
            });
        });
    }
    removeProjectsInFolder(folder) {
        const projects = this.projectsByFolderUri.get(folder.uri);
        if (projects) {
            projects.forEach((project) => project.clearAllDiagnostics());
            this.projectsByFolderUri.delete(folder.uri);
        }
    }
    get projects() {
        return Array.from(this.projectsByFolderUri.values()).flat();
    }
    projectForFile(uri) {
        const cachedResult = this._projectForFileCache.get(uri);
        if (cachedResult) {
            return cachedResult;
        }
        for (const projects of this.projectsByFolderUri.values()) {
            const project = projects.find((project) => project.includesFile(uri));
            if (project) {
                this._projectForFileCache.set(uri, project);
                return project;
            }
        }
        return undefined;
    }
}
exports.GraphQLWorkspace = GraphQLWorkspace;
//# sourceMappingURL=workspace.js.map