@ima/core
Version:
IMA.js framework for isomorphic javascript application
196 lines (195 loc) • 7.4 kB
JavaScript
import { autoYield } from '@esmj/task';
import * as helpers from '@ima/helpers';
import { ns } from './Namespace';
import { BindingState } from './oc/BindingState';
import { Router } from './router/Router';
/**
* Application bootstrap used to initialize the environment and the application
* itself.
*/ export class Bootstrap {
_oc;
_config;
/**
* Initializes the bootstrap.
*
* @param oc The application's object container to use
* for managing dependencies.
*/ constructor(oc){
/**
* The object container used to manage dependencies.
*/ this._oc = oc;
}
/**
* Initializes the application by running the bootstrap sequence. The
* sequence initializes the components of the application in the following
* order:
* - application settings
* - constants, service providers and class dependencies configuration
* - services
* - UI components
* - routing
*
* @param config The application environment
* configuration for the current environment.
*/ async run(config) {
this._config = config;
await autoYield();
this._initSettings();
await autoYield();
await this._bindDependencies();
await autoYield();
await this._initServices();
await autoYield();
this._initRoutes();
}
/**
* Initializes dynamically loaded plugin. This is explicitly called from
* within the Plugin Loader instance.
*
* @param name Plugin name.
* @param plugin Plugin interface (object with init functions).
*/ async initPlugin(name, plugin) {
if (!plugin) {
return;
}
await autoYield();
this._initPluginSettings(name, plugin);
await autoYield();
this._bindPluginDependencies(name, plugin);
await autoYield();
this._initPluginServices(plugin);
}
/**
* Initializes the application settings. The method loads the settings for
* all environments and then picks the settings for the current environment.
*
* The method also handles using the values in the production environment
* as default values for configuration items in other environments.
*/ _initSettings() {
/**
* Default settings for the application.
*/ const currentApplicationSettings = {
$Http: {
defaultRequestOptions: {
timeout: 15000,
repeatRequest: 1,
ttl: 60000,
fetchOptions: {
mode: 'cors',
headers: {
Accept: 'application/json'
}
},
validateCookies: false,
cache: true
},
cacheOptions: {
prefix: 'http.'
}
},
$Router: {
middlewareTimeout: 30000
},
$Cache: {
enabled: true,
ttl: 60000
}
};
const settings = {};
const plugins = this._config.plugins.concat([
{
name: BindingState.App,
plugin: this._config
}
]);
plugins.filter(({ plugin })=>typeof plugin.initSettings === 'function').forEach(({ name, plugin })=>{
const allPluginSettings = plugin.initSettings(ns, this._oc, this._config.settings, false);
helpers.assignRecursivelyWithTracking(name)(settings, currentApplicationSettings, helpers.resolveEnvironmentSetting(allPluginSettings, this._config.settings.$Env));
});
this._config.bind = {
...this._config.bind,
...settings,
...this._config.settings
};
}
/**
* Initializes dynamically loaded plugin settings (if the init
* function is provided). The settings are merged into the application
* the same way as with non-dynamic import, meaning the app setting overrides
* are prioritized over the default plugin settings.
*
* @param name Plugin name.
* @param plugin Plugin interface (object with init functions).
*/ _initPluginSettings(name, plugin) {
if (typeof plugin?.initSettings !== 'function') {
return;
}
const newApplicationSettings = {};
const allPluginSettings = plugin.initSettings(ns, this._oc, this._config.settings, true // Indicating static dynamic bootstraping
);
helpers.assignRecursivelyWithTracking(name)(newApplicationSettings, helpers.resolveEnvironmentSetting(allPluginSettings, this._config.settings.$Env));
helpers.assignRecursivelyWithTracking(BindingState.App)(newApplicationSettings, this._config.bind);
this._config.bind = {
...this._config.bind,
...newApplicationSettings
};
}
/**
* Binds the constants, service providers and class dependencies to the
* object container.
*/ async _bindDependencies() {
this._oc.setBindingState(BindingState.IMA);
this._config.initBindIma(ns, this._oc, this._config.bind, BindingState.IMA);
const filteredPlugins = this._config.plugins.filter(({ plugin })=>typeof plugin.initBind === 'function');
for (const { name, plugin } of filteredPlugins){
await autoYield();
this._oc.setBindingState(BindingState.Plugin, name);
plugin.initBind(ns, this._oc, this._config.bind, false);
}
this._oc.setBindingState(BindingState.App);
this._config.initBindApp(ns, this._oc, this._config.bind, BindingState.App);
}
/**
* Binds the constants, service providers and class dependencies to the
* object container for dynamically imported plugins.
*
* @param name Plugin name.
* @param plugin Plugin interface (object with init functions).
*/ _bindPluginDependencies(name, plugin) {
if (typeof plugin.initBind !== 'function') {
return;
}
this._oc.setBindingState(BindingState.Plugin, name);
plugin.initBind(ns, this._oc, this._config.bind, true, name);
this._oc.setBindingState(BindingState.App);
}
/**
* Initializes the routes.
*/ _initRoutes() {
const router = this._oc.get(Router);
this._config.initRoutes(ns, this._oc, this._config.routes, router);
}
/**
* Initializes the basic application services.
*/ async _initServices() {
this._config.initServicesIma(ns, this._oc, this._config.services);
const filteredPlugins = this._config.plugins.filter(({ plugin })=>typeof plugin.initServices === 'function');
for (const { plugin } of filteredPlugins){
await autoYield();
plugin.initServices(ns, this._oc, this._config.services, false);
}
this._config.initServicesApp(ns, this._oc, this._config.services);
}
/**
* Service initialization for the dynamically loaded plugins.
*
* @param plugin Plugin interface (object with init functions).
*/ _initPluginServices(plugin) {
if (typeof plugin.initServices !== 'function') {
return;
}
plugin.initServices(ns, this._oc, this._config.services, true);
}
}
ns.set('ns.ima.core.Bootstrap', Bootstrap);
//# sourceMappingURL=Bootstrap.js.map