UNPKG

@teqfw/core

Version:

Core functionalities for plugin scanning, local configuration management, and command-line utilities for building modern web applications with the Tequila Framework.

133 lines (122 loc) 4.58 kB
/** * Order an items using it's before/after attributes. * * !!! Don't use this code as singleton. Create or reset instances for every usage. !!! */ // MODULE'S VARS const NS = 'TeqFw_Core_Shared_Util_BeforeAfter'; // MODULE'S CLASSES /** * @memberOf TeqFw_Core_Shared_Util_BeforeAfter */ class Dto { static namespace = NS; /** @type {string[]} */ after; /** @type {string[]} */ before; /** @type {string} */ id; } export default class TeqFw_Core_Shared_Util_BeforeAfter { constructor() { // VARS let _needReorder = false; /** @type {Object<string, Dto>} */ let _items = {}; let _ordered = []; // FUNCS function reorder() { // FUNCS /** * Convert 'before/after' notation to 'dependent' notation and compose successors list. * @returns Object<string, string[]> */ function initSuccessors() { /** @type {Object<string, string[]>} */ const res = {}; // {root => [node, ...]} for (const id of Object.keys(_items)) { if (!res[id]) res[id] = []; const item = _items[id]; // node / this for (const one of item.after) res[id].push(one); // this / root for (const one of item.before) { if (!res[one]) res[one] = []; res[one].push(id); } } // make deps unique and delete nodes w/o dependants for (const ns of Object.keys(res)) { res[ns] = [...new Set(res[ns])]; if (res[ns].length === 0) delete res[ns]; } return res; } /** * Recursive function to update nodes weights in hierarchy. * 1 - node has no deps (root), 2 - node has one dep's level below, ... * * Circular dependencies resolution is out of this scope. * * @param {string} name * @param {number} weight * @param {Object<string, string[]>} successors root => [node1, ...] * @param {Object<string, number>} weights */ function setWeights(name, weight, successors, weights) { if (weights[name]) weight = weights[name] + 1; if (successors[name]) for (const one of successors[name]) if (weights[one]) setWeights(one, weights[one] + 1, successors, weights); else setWeights(one, 1, successors, weights); weights[name] = weight; } // MAIN _ordered = []; /** @type {Object<string, string[]>} {root => [node, ...]} */ const successors = initSuccessors(); /** @type {Object<string, number>} */ const weights = {}; for (const one of Object.values(_items)) setWeights(one.id, 1, successors, weights); // convert weights to levels, some items may be on one level /** @type {Object<number, string[]>} */ const byLevel = {}; for (const name of Object.keys(weights)) { const weight = weights[name]; if (!byLevel[weight]) byLevel[weight] = []; byLevel[weight].push(name); } // reverse levels (first level is a root and should be mapped at the end const keys = Object.keys(byLevel).map(key => parseInt(key)); // get keys as integers keys.sort((a, b) => b - a); // sort as numbers (9..0) for (const one of keys) for (const id of byLevel[one]) if (_items[id]) _ordered.push(id); _needReorder = false; return _ordered; } // INSTANCE METHODS /** * @param {TeqFw_Core_Shared_Util_BeforeAfter.Dto} item */ this.addItem = function (item) { _items[item.id] = item; _needReorder = true; } this.getOrdered = function () { if (_needReorder) reorder(); return [..._ordered]; // return copy } this.reset = function () { _needReorder = false; _items = {}; _ordered = []; } } } export { Dto }