UNPKG

sententiaregum-flux-container

Version:

The flux implementation of the sententiaregum project based on facebook/flux

161 lines (142 loc) 5.78 kB
/* * This file is part of the Sententiaregum project. * * (c) Maximilian Bosch <maximilian.bosch.27@gmail.com> * (c) Ben Bieler <benjaminbieler2014@gmail.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /** * Sorts the given listeners by its order. * * @param {Object} listeners The listeners to be triggered at one event. * * @returns {Array.<Function>} The ordered callback chain. * @private This is part of the internal API and should not be used directly! */ exports.default = function (listeners) { var _store = listeners, _ids = Object.keys(_store); /** * Creates a graph of dependencies based on a given base. * * The graph computer tries to find non-managed dependencies (root dependency or not required by an already processed one) * and then its dependencies will be evaluated and appended to the execution tree. * * @param {Array.<String>} dependencies List of dependencies of the base listener. * @param {Array.<String>} known List of already processed items in order to avoid a cycle. * @param {Array} currentGraphSnippet Snapshot of the base graph. * @param {Array} parentGraph Snapshot of the graph above the current token. * * @returns {Array.<String>} The dependency graph based on */ function createDependencySubgraph(dependencies, known, currentGraphSnippet, parentGraph) { var graph = []; dependencies.forEach(function (token) { checkEdges(token, known); if (!isProcessed(token, currentGraphSnippet) && !isProcessed(token, parentGraph) && !isDuplicated(token, graph)) { var config = _store[token]; if (config.dependencies.length > 0) { graph = graph.concat(createDependencySubgraph(config.dependencies, known.concat([token]), currentGraphSnippet, graph)); } graph.push(config); } }); return graph; } /** * Checks if the an edge-token is corrupted or already processed. * * Validates the edges and ensures that no edge will be duplicated as this'll cause circular references and * endless loops. * * @param token * @param {Array.<String>} known List of already * * @returns {void} */ function checkEdges(token, known) { (0, _invariant2.default)(-1 !== _ids.indexOf(token), 'Token "' + token + '" is not registered as listener!'); (0, _invariant2.default)(-1 === known.indexOf(token), 'Circular reference detected: Token "' + token + '" is already processed!'); } /** * Checks whether the given dependency is used by other items. * * @param {String} token ID. * * @returns {boolean} Whether or not. */ function isForeignDependency(token) { return isProcessed(token, _ids.map(function (key) { return _store[key]; })); } /** * Checks whether the item is already processed. * * This is necessary in order to avoid duplicated executions during the graph computation. * It iterates over the dependencies of items present in the graph or sub-graph provided as second argument * and checks if the token is already processed in this graph as dependency. * * @param {String} token The token. * @param {Array} graph The current graph. * * @returns {boolean} */ function isProcessed(token, graph) { var _ref; return (_ref = []).concat.apply(_ref, _toConsumableArray(graph.map(function (data) { return data.dependencies; }))).indexOf(token) !== -1; } /** * Checks whether one item is duplicated. * * It might be possible that multiple roots exist and both need a certain callback. To achieve that, this callback * must be executed before, but MUST not be executed both. * One chain might look like this: * * A --> B <-- C * * In this case A and C would be roots and both would include B in its subtree, but it should be avoided to execute * it two times. * * @param {String} token The token to validate. * @param {Array} graph The dependency graph. * * @returns {boolean} Whether or not duplicated. */ function isDuplicated(token, graph) { return !graph.every(function (data) { return data !== _store[token]; }); } // build execution tree var result = Object.keys(_store).reduce(function (prev, id) { if (isForeignDependency(id) || isProcessed(id, prev)) { return prev; } var data = listeners[id]; return prev.concat(createDependencySubgraph(data.dependencies, [], prev, []).concat([data])); }, []); // there's an absolute length of IDs and if it doesn't match the length of the execution // tree not all callbacks are managed. This might happen if a dependency cycle exists which // look looks like this: A -> B -> A. // In this case the resolver can't figure out where to start as the first dependency in the chain must // not have any dependencies. (0, _invariant2.default)(_ids.length === result.length, 'No root dependency detected!'); // Internally a config object is built, but the dispatcher expects no configuration created by the // private API, but a flat list of callbacks. return result.map(function (config) { return config.callback; }); };