kibana-123
Version:
Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic
128 lines (113 loc) • 4.43 kB
JavaScript
import angular from 'angular';
import _ from 'lodash';
/**
* This module is used by Kibana to create and reuse angular modules. Angular modules
* can only be created once and need to have their dependencies at creation. This is
* hard/impossible to do in require.js since all of the dependencies for a module are
* loaded before it is.
*
* Here is an example:
*
* In the scenario below, require.js would load directive.js first because it is a
* dependency of app.js. This would cause the call to `angular.module('app')` to
* execute before the module is actually created. This causes angular to throw an
* error. This effect is magnified when app.js links off to many different modules.
*
* This is normally solved by creating unique modules per file, listed as the 1st
* alternate solution below. Unfortunately this solution would have required that
* we replicate our require statements.
*
* app.js
* ```
* angular.module('app', ['ui.bootstrap'])
* .controller('AppController', function () { ... });
*
* require('./directive');
* ```
*
* directive.js
* ```
* angular.module('app')
* .directive('someDirective', function () { ... });
* ```
*
* Before taking this approach we saw three possible solutions:
* 1. replicate our js modules in angular modules/use a different module per file
* 2. create a single module outside of our js modules and share it
* 3. use a helper lib to dynamically create modules as needed.
*
* We decided to go with #3
*
* This ends up working by creating a list of modules that the code base creates by
* calling `modules.get(name)` with different names, and then before bootstrapping
* the application kibana uses `modules.link()` to set the dependencies of the "kibana"
* module to include every defined module. This guarantees that kibana can always find
* any angular dependecy defined in the kibana code base. This **also** means that
* Private modules are able to find any dependency, since they are injected using the
* "kibana" module's injector.
*
*/
const existingModules = {};
const links = [];
/**
* Take an angular module and extends the dependencies for that module to include all of the modules
* created using `ui/modules`
*
* @param {AngularModule} module - the module to extend
* @return {undefined}
*/
function link(module) {
// as modules are defined they will be set as requirements for this app
links.push(module);
// merge in the existing modules
module.requires = _.union(module.requires, _.keys(existingModules));
}
/**
* The primary means of interacting with `ui/modules`. Returns an angular module. If the module already
* exists the existing version will be returned. `dependencies` are either set as or merged into the
* modules total dependencies.
*
* This is in contrast to the `angular.module(name, [dependencies])` function which will only
* create a module if the `dependencies` list is passed and get an existing module if no dependencies
* are passed. This requires knowing the order that your files will load, which we can't guarantee.
*
* @param {string} moduleName - the unique name for this module
* @param {array[string]} [requires=[]] - the other modules this module requires
* @return {AngularModule}
*/
function get(moduleName, requires) {
let module = existingModules[moduleName];
if (module === void 0) {
// create the module
module = existingModules[moduleName] = angular.module(moduleName, []);
module.close = _.partial(close, moduleName);
// ensure that it is required by linked modules
_.each(links, function (app) {
if (!~app.requires.indexOf(moduleName)) app.requires.push(moduleName);
});
}
if (requires) {
// update requires list with possibly new requirements
module.requires = _.union(module.requires, requires);
}
return module;
}
function close(moduleName) {
const module = existingModules[moduleName];
// already closed
if (!module) return;
// if the module is currently linked, unlink it
const i = links.indexOf(module);
if (i > -1) links.splice(i, 1);
// remove from linked modules list of required modules
_.each(links, function (app) {
_.pull(app.requires, moduleName);
});
// remove module from existingModules
delete existingModules[moduleName];
}
export default {
link: link,
get: get,
close: close
};