UNPKG

@eclipse-glsp/protocol

Version:

The protocol definition for client-server communication in GLSP

120 lines (108 loc) 5.07 kB
/******************************************************************************** * Copyright (c) 2023-2024 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ import { ContainerModule, interfaces } from 'inversify'; import { MaybeArray, asArray } from '../utils/array-util'; import { BindingContext } from './inversify-util'; /** * Optional constructor options for {@link FeatureModule}s. */ export interface FeatureModuleOptions { /** * The set of feature modules that is required in order for this module to load. */ requires?: MaybeArray<FeatureModule>; /** * Optional `featureId` that should be used. If omitted an id will be autogenerated */ featureId?: symbol; } /** * A `FeatureModule` is a specialized {@link ContainerModule} that can declare dependencies to other {@link FeatureModule}. * A feature module will only be loaded into a container if all of its required modules haven been loaded before. T * Each feature module binds its `featureId` be default. This enables querying of existing container to check wether a * feature module has been loaded into this container. */ export class FeatureModule extends ContainerModule { /** * Global flag to enable/disable additional debug log output when loading feature modules * Default is `false`. */ public static DEBUG_LOG_ENABLED = false; readonly featureId: symbol; readonly requires?: MaybeArray<FeatureModule>; constructor(registry: interfaces.ContainerModuleCallBack, options: FeatureModuleOptions = {}) { super((bind, unbind, isBound, ...rest) => { if (this.configure(bind, isBound)) { registry(bind, unbind, isBound, ...rest); this.debugLog(`Loading of feature module with id '${this.featureId.toString()}' completed`); } }); this.featureId = options.featureId ?? this.createFeatureId(); this.requires = options.requires; } protected createFeatureId(): symbol { return Symbol(this.id); } /** * Configures the feature module i.e. checks if the requirements are met. * If this is the case the {@link FeatureModule.featureId} will be bound and the module will be loaded * @param bind container bind function * @param isBound container isBound function * @returns `true` if all requirements are met and the module is loaded. `false` otherwise */ configure(bind: interfaces.Bind, isBound: interfaces.IsBound): boolean { this.debugLog(`Trying to load feature module with id '${this.featureId.toString()}'`); if (this.isLoaded({ isBound })) { const message = `Could not load feature module. Another module with id '${this.featureId.toString()}' is already loaded`; this.debugLog(message); throw new Error(message); } if (this.checkRequirements(isBound)) { this.debugLog(`Requirements are met, continue loading of feature module with id '${this.featureId.toString()}'`); bind(this.featureId).toConstantValue(this.featureId); return true; } return false; } protected debugLog(message?: any, ...optionalParams: any[]): void { if (FeatureModule.DEBUG_LOG_ENABLED) { console.log(message, ...optionalParams); } } /** * Checks if all required {@link FeatureModule}s are already loaded/bound in the container. * @param isBound The `isBound` property of the module callback. Used to check the required modules. * @returns `true` if all requirements are met, `false` otherwise */ protected checkRequirements(isBound: interfaces.IsBound): boolean { const requires = asArray(this.requires ?? []); if (requires.length === 0) { return true; } const missing = requires.filter(module => !module.isLoaded({ isBound })); if (missing.length > 0) { this.debugLog( // eslint-disable-next-line max-len `Could not load feature module. Required modules are not loaded. Feature ids: ${missing.map(m => m.featureId.toString()).join(', ')}` ); return false; } return true; } isLoaded(context: Pick<BindingContext, 'isBound'>): boolean { return context.isBound(this.featureId); } }