UNPKG

langium

Version:

A language engineering tool for the Language Server Protocol

164 lines 7.19 kB
/****************************************************************************** * Copyright 2021 TypeFox GmbH * This program and the accompanying materials are made available under the * terms of the MIT License, which is available in the project root. ******************************************************************************/ export var Module; (function (Module) { /** * Merges two dependency injection modules into a new (third) one that is returned. * At that `m1` and `m2` stay unchanged. Therefore, `m1` is deep-copied first, * and m2 is merged onto the copy afterwards. * * Note that the leaf values of `m1` and `m2`, i.e. the service constructor functions, * cannot be copied generically, since they are functions. They are shared by the source and merged modules. * * @returns the merged module being a deep copy of `m1` with `m2` merged onto it. */ Module.merge = (m1, m2) => _merge(_merge({}, m1), m2); })(Module || (Module = {})); /** * Given a set of modules, the inject function returns a lazily evaluated injector * that injects dependencies into the requested service when it is requested the * first time. Subsequent requests will return the same service. * * In the case of cyclic dependencies, an Error will be thrown. This can be fixed * by injecting a provider `() => T` instead of a `T`. * * Please note that the arguments may be objects or arrays. However, the result will * be an object. Using it with for..of will have no effect. * * @param module1 first Module * @param module2 (optional) second Module * @param module3 (optional) third Module * @param module4 (optional) fourth Module * @param module5 (optional) fifth Module * @param module6 (optional) sixth Module * @param module7 (optional) seventh Module * @param module8 (optional) eighth Module * @param module9 (optional) ninth Module * @returns a new object of type I */ export function inject(module1, module2, module3, module4, module5, module6, module7, module8, module9) { const module = [module1, module2, module3, module4, module5, module6, module7, module8, module9].reduce(_merge, {}); return _inject(module); } const isProxy = Symbol('isProxy'); /** * Eagerly load all services in the given dependency injection container. This is sometimes * necessary because services can register event listeners in their constructors. */ export function eagerLoad(item) { if (item && item[isProxy]) { for (const value of Object.values(item)) { eagerLoad(value); } } return item; } /** * Helper function that returns an injector by creating a proxy. * Invariant: injector is of type I. If injector is undefined, then T = I. */ function _inject(module, injector) { const proxy = new Proxy({}, { deleteProperty: () => false, set: () => { throw new Error('Cannot set property on injected service container'); }, get: (obj, prop) => { if (prop === isProxy) { return true; } else { return _resolve(obj, prop, module, injector || proxy); } }, getOwnPropertyDescriptor: (obj, prop) => (_resolve(obj, prop, module, injector || proxy), Object.getOwnPropertyDescriptor(obj, prop)), // used by for..in has: (_, prop) => prop in module, // used by ..in.. ownKeys: () => [...Object.getOwnPropertyNames(module)] // used by for..in }); return proxy; } /** * Internally used to tag a requested dependency, directly before calling the factory. * This allows us to find cycles during instance creation. */ const __requested__ = Symbol(); /** * Returns the value `obj[prop]`. If the value does not exist, yet, it is resolved from * the module description. The result of service factories is cached. Groups are * recursively proxied. * * @param obj an object holding all group proxies and services * @param prop the key of a value within obj * @param module an object containing groups and service factories * @param injector the first level proxy that provides access to all values * @returns the requested value `obj[prop]` * @throws Error if a dependency cycle is detected */ function _resolve(obj, prop, module, injector) { if (prop in obj) { if (obj[prop] instanceof Error) { throw new Error('Construction failure. Please make sure that your dependencies are constructable. Cause: ' + obj[prop]); } if (obj[prop] === __requested__) { throw new Error('Cycle detected. Please make "' + String(prop) + '" lazy. Visit https://langium.org/docs/reference/configuration-services/#resolving-cyclic-dependencies'); } return obj[prop]; } else if (prop in module) { const value = module[prop]; obj[prop] = __requested__; try { obj[prop] = (typeof value === 'function') ? value(injector) : _inject(value, injector); } catch (error) { obj[prop] = error instanceof Error ? error : undefined; throw error; } return obj[prop]; } else { return undefined; } } /** * Performs a deep-merge of two modules by writing source entries into the target module. * * @param target the module which is written * @param source the module which is read * @returns the target module */ function _merge(target, source) { if (source) { for (const [key, sourceValue] of Object.entries(source)) { if (sourceValue !== undefined && sourceValue !== null) { if (typeof sourceValue === 'object') { const targetValue = target[key]; if (typeof targetValue === 'object' && targetValue !== null) { // in case both values are real (non-null) objects merge them recursively target[key] = _merge(targetValue, sourceValue); } else { // in case 'target[key]' is not a non-null object // we overwrite any existing value with a deep copy of 'sourceValue' // by recursively calling this function with a new 'target' object to be populated // that is assigned to 'target[key]' afterwards target[key] = _merge({}, sourceValue); } } else { // in case 'sourceValue' is defined and assigned (non-null) but not an object // we assume it to be a service constructor function according to the Module<I> type definition target[key] = sourceValue; // note the following for such service constructor functions: // 'target[key]' will now reference the same function object being referenced by 'source[key]'. // This is accepted here, since function objects cannot be safely copied in general. } } } } return target; } //# sourceMappingURL=dependency-injection.js.map