UNPKG

@feature-hub/core

Version:

Create scalable web applications using micro frontends.

221 lines 9.87 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; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FeatureAppManager = void 0; const async_value_1 = require("./async-value"); const Messages = __importStar(require("./internal/feature-app-manager-messages")); const is_feature_app_module_1 = require("./internal/is-feature-app-module"); /** * The `FeatureAppManager` manages the lifecycle of Feature Apps. */ class FeatureAppManager { constructor(featureServiceRegistry, options = {}) { this.featureServiceRegistry = featureServiceRegistry; this.options = options; this.asyncFeatureAppDefinitions = new Map(); this.featureAppDefinitionsWithRegisteredOwnFeatureServices = new WeakSet(); this.featureAppRetainers = new Map(); this.logger = options.logger || console; } /** * Load a [[FeatureAppDefinition]] using the module loader the * [[FeatureAppManager]] was initilized with. * * @throws Throws an error if no module loader was provided on initilization. * * @param url A URL pointing to a [[FeatureAppDefinition]] bundle in a module * format compatible with the module loader. * * @param moduleType The module type of the [[FeatureAppDefinition]] bundle. * This value can be used by the provided * [[FeatureAppManagerOptions.moduleLoader]]. * * @returns An [[AsyncValue]] containing a promise that resolves with the * loaded [[FeatureAppDefinition]]. If called again with the same URL it * returns the same [[AsyncValue]]. The promise rejects when loading fails, or * when the loaded bundle doesn't export a [[FeatureAppDefinition]] as * default. */ getAsyncFeatureAppDefinition(url, moduleType) { const key = `${url}${moduleType}`; let asyncFeatureAppDefinition = this.asyncFeatureAppDefinitions.get(key); if (!asyncFeatureAppDefinition) { asyncFeatureAppDefinition = this.createAsyncFeatureAppDefinition(url, moduleType); this.asyncFeatureAppDefinitions.set(key, asyncFeatureAppDefinition); } return asyncFeatureAppDefinition; } /** * Create a [[FeatureAppScope]] which includes validating externals, binding * all available Feature Service dependencies, and calling the `create` method * of the [[FeatureAppDefinition]]. * * @throws Throws an error if Feature Services that the * [[FeatureAppDefinition]] provides with its `ownFeatureServices` key fail to * be registered. * @throws Throws an error if the required externals can't be satisfied. * @throws Throws an error if the required Feature Services can't be * satisfied. * @throws Throws an error the [[FeatureAppDefinition]]'s `create` method * throws. * * @param featureAppID The ID of the Feature App to create a scope for. * @param featureAppDefinition The definition of the Feature App to create a * scope for. * * @returns A [[FeatureAppScope]] for the provided Feature App ID and * [[FeatureAppDefinition]]. A new scope is created for every call of * `createFeatureAppScope`, even with the same ID and definiton. */ createFeatureAppScope(featureAppId, featureAppDefinition, options = {}) { const featureAppRetainer = this.getFeatureAppRetainer(featureAppId, featureAppDefinition, options); let released = false; return { featureApp: featureAppRetainer.featureApp, release: () => { if (released) { this.logger.warn(`The Feature App with the ID ${JSON.stringify(featureAppId)} has already been released for this scope.`); } else { released = true; featureAppRetainer.release(); } }, }; } /** * Preload a [[FeatureAppDefinition]] using the module loader the * [[FeatureAppManager]] was initilized with. Useful before hydration of a * server rendered page to avoid render result mismatch between client and * server due missing [[FeatureAppDefinition]]s. * * @throws Throws an error if no module loader was provided on initilization. * * @param url A URL pointing to a [[FeatureAppDefinition]] bundle in a module * format compatible with the module loader. * * @param moduleType The module type of the [[FeatureAppDefinition]] bundle. * This value can be used by the provided * [[FeatureAppManagerOptions.moduleLoader]]. */ async preloadFeatureApp(url, moduleType) { await this.getAsyncFeatureAppDefinition(url, moduleType).promise; } createAsyncFeatureAppDefinition(url, moduleType) { const { moduleLoader: loadModule } = this.options; if (!loadModule) { throw new Error('No module loader provided.'); } return new async_value_1.AsyncValue(loadModule(url, moduleType).then((featureAppModule) => { if (!(0, is_feature_app_module_1.isFeatureAppModule)(featureAppModule)) { throw new Error(Messages.invalidFeatureAppModule(url, moduleType, this.options.moduleLoader)); } this.logger.info(`The Feature App module at the url ${JSON.stringify(url)} has been successfully loaded.`); return featureAppModule.default; })); } registerOwnFeatureServices(featureAppId, featureAppDefinition) { if (this.featureAppDefinitionsWithRegisteredOwnFeatureServices.has(featureAppDefinition)) { return; } if (featureAppDefinition.ownFeatureServiceDefinitions) { this.featureServiceRegistry.registerFeatureServices(featureAppDefinition.ownFeatureServiceDefinitions, featureAppId); } this.featureAppDefinitionsWithRegisteredOwnFeatureServices.add(featureAppDefinition); } getFeatureAppRetainer(featureAppId, featureAppDefinition, options) { let featureAppRetainer = this.featureAppRetainers.get(featureAppId); if (featureAppRetainer) { featureAppRetainer.retain(); } else { this.registerOwnFeatureServices(featureAppId, featureAppDefinition); featureAppRetainer = this.createFeatureAppRetainer(featureAppDefinition, featureAppId, options); } return featureAppRetainer; } createFeatureAppRetainer(featureAppDefinition, featureAppId, options) { var _a, _b; this.validateExternals(featureAppDefinition, featureAppId); const { featureAppName, baseUrl, beforeCreate, config, done, parentFeatureApp, } = options; const binding = this.featureServiceRegistry.bindFeatureServices(featureAppDefinition, featureAppId, featureAppName); try { (_b = (_a = this.options).onBind) === null || _b === void 0 ? void 0 : _b.call(_a, { featureAppDefinition, featureAppId, featureAppName, parentFeatureApp, }); } catch (error) { this.logger.error('Failed to execute onBind callback.', error); } const env = { baseUrl, config, featureAppId, featureAppName, featureServices: binding.featureServices, done, }; try { beforeCreate === null || beforeCreate === void 0 ? void 0 : beforeCreate(env); const featureApp = featureAppDefinition.create(env); this.logger.info(`The Feature App with the ID ${JSON.stringify(featureAppId)} has been successfully created.`); let retainCount = 1; const featureAppRetainer = { featureApp, retain: () => { retainCount += 1; }, release: () => { retainCount -= 1; if (retainCount === 0) { this.featureAppRetainers.delete(featureAppId); binding.unbind(); } }, }; this.featureAppRetainers.set(featureAppId, featureAppRetainer); return featureAppRetainer; } catch (error) { binding.unbind(); throw error; } } validateExternals(featureAppDefinition, featureAppId) { const { externalsValidator } = this.options; if (!externalsValidator) { return; } const { dependencies } = featureAppDefinition; if (dependencies && dependencies.externals) { externalsValidator.validate(dependencies.externals, featureAppId); } } } exports.FeatureAppManager = FeatureAppManager; //# sourceMappingURL=feature-app-manager.js.map