| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 | 
							- /*
 
- 	MIT License http://www.opensource.org/licenses/mit-license.php
 
- 	Author Tobias Koppers @sokra
 
- */
 
- "use strict";
 
- const path = require("path");
 
- /**
 
-  * @template T
 
-  * @typedef {Object} TreeNode
 
-  * @property {string} filePath
 
-  * @property {TreeNode} parent
 
-  * @property {TreeNode[]} children
 
-  * @property {number} entries
 
-  * @property {boolean} active
 
-  * @property {T[] | T | undefined} value
 
-  */
 
- /**
 
-  * @template T
 
-  * @param {Map<string, T[] | T} plan
 
-  * @param {number} limit
 
-  * @returns {Map<string, Map<T, string>>} the new plan
 
-  */
 
- module.exports = (plan, limit) => {
 
- 	const treeMap = new Map();
 
- 	// Convert to tree
 
- 	for (const [filePath, value] of plan) {
 
- 		treeMap.set(filePath, {
 
- 			filePath,
 
- 			parent: undefined,
 
- 			children: undefined,
 
- 			entries: 1,
 
- 			active: true,
 
- 			value
 
- 		});
 
- 	}
 
- 	let currentCount = treeMap.size;
 
- 	// Create parents and calculate sum of entries
 
- 	for (const node of treeMap.values()) {
 
- 		const parentPath = path.dirname(node.filePath);
 
- 		if (parentPath !== node.filePath) {
 
- 			let parent = treeMap.get(parentPath);
 
- 			if (parent === undefined) {
 
- 				parent = {
 
- 					filePath: parentPath,
 
- 					parent: undefined,
 
- 					children: [node],
 
- 					entries: node.entries,
 
- 					active: false,
 
- 					value: undefined
 
- 				};
 
- 				treeMap.set(parentPath, parent);
 
- 				node.parent = parent;
 
- 			} else {
 
- 				node.parent = parent;
 
- 				if (parent.children === undefined) {
 
- 					parent.children = [node];
 
- 				} else {
 
- 					parent.children.push(node);
 
- 				}
 
- 				do {
 
- 					parent.entries += node.entries;
 
- 					parent = parent.parent;
 
- 				} while (parent);
 
- 			}
 
- 		}
 
- 	}
 
- 	// Reduce until limit reached
 
- 	while (currentCount > limit) {
 
- 		// Select node that helps reaching the limit most effectively without overmerging
 
- 		const overLimit = currentCount - limit;
 
- 		let bestNode = undefined;
 
- 		let bestCost = Infinity;
 
- 		for (const node of treeMap.values()) {
 
- 			if (node.entries <= 1 || !node.children || !node.parent) continue;
 
- 			if (node.children.length === 0) continue;
 
- 			if (node.children.length === 1 && !node.value) continue;
 
- 			// Try to select the node with has just a bit more entries than we need to reduce
 
- 			// When just a bit more is over 30% over the limit,
 
- 			// also consider just a bit less entries then we need to reduce
 
- 			const cost =
 
- 				node.entries - 1 >= overLimit
 
- 					? node.entries - 1 - overLimit
 
- 					: overLimit - node.entries + 1 + limit * 0.3;
 
- 			if (cost < bestCost) {
 
- 				bestNode = node;
 
- 				bestCost = cost;
 
- 			}
 
- 		}
 
- 		if (!bestNode) break;
 
- 		// Merge all children
 
- 		const reduction = bestNode.entries - 1;
 
- 		bestNode.active = true;
 
- 		bestNode.entries = 1;
 
- 		currentCount -= reduction;
 
- 		let parent = bestNode.parent;
 
- 		while (parent) {
 
- 			parent.entries -= reduction;
 
- 			parent = parent.parent;
 
- 		}
 
- 		const queue = new Set(bestNode.children);
 
- 		for (const node of queue) {
 
- 			node.active = false;
 
- 			node.entries = 0;
 
- 			if (node.children) {
 
- 				for (const child of node.children) queue.add(child);
 
- 			}
 
- 		}
 
- 	}
 
- 	// Write down new plan
 
- 	const newPlan = new Map();
 
- 	for (const rootNode of treeMap.values()) {
 
- 		if (!rootNode.active) continue;
 
- 		const map = new Map();
 
- 		const queue = new Set([rootNode]);
 
- 		for (const node of queue) {
 
- 			if (node.active && node !== rootNode) continue;
 
- 			if (node.value) {
 
- 				if (Array.isArray(node.value)) {
 
- 					for (const item of node.value) {
 
- 						map.set(item, node.filePath);
 
- 					}
 
- 				} else {
 
- 					map.set(node.value, node.filePath);
 
- 				}
 
- 			}
 
- 			if (node.children) {
 
- 				for (const child of node.children) {
 
- 					queue.add(child);
 
- 				}
 
- 			}
 
- 		}
 
- 		newPlan.set(rootNode.filePath, map);
 
- 	}
 
- 	return newPlan;
 
- };
 
 
  |