UNPKG

4.45 kBJavaScriptView Raw
1// flattens a bundle graph
2var winston = require('winston');
3var name = require('./name');
4var size = require('../node/size');
5var bundle = {
6 /**
7 * Flattens the list of shares until each script has a minimal depth
8 * @param {Object} shares
9 * @param {Object} depth
10 */
11 flatten: function(shares, depth){
12 // make waste object
13 // mark the size
14 while(bundle.maxDepth(shares) > depth){
15 var min = bundle.min(shares, depth);
16 if(min) {
17 bundle.merge(shares, min);
18 }
19 }
20 },
21 /**
22 * Merges 2 shares contents. Shares are expected to be in the order
23 * getMostShared removes them ... by lowest depenency first.
24 * We should merge into the 'lower' dependency.
25 *
26 * @param {Object} shares
27 * @param {Object} min
28 *
29 * diff : {app1 : waste, app2 : waste, _waste: 0},
30 * lower: i, - the 'lower' share whos contents will be merged into, and contents should run first
31 * higher: j - the 'higher' share
32 */
33 merge: function(shares, min){
34 var lower = shares[min.lower],
35 upper = shares[min.higher];
36
37 winston.debug(" flattening "+name.getName(upper)+">"+name.getName(lower));
38
39 for(var appName in min.diff.bundleToWaste){
40 if( min.diff.bundleToWaste[appName] ){
41 winston.debug(" + "+min.diff[appName]+" "+appName);
42 }
43 }
44
45 // remove old one
46 shares.splice(min.higher, 1);
47
48 // merge in files, lowers should run first
49 lower.nodes = lower.nodes.concat(upper.nodes);
50
51 // merge in apps
52 var apps = this.appsHash(lower);
53 upper.bundles.forEach(function(appName){
54 if(!apps[appName]){
55 lower.bundles.push(appName);
56 }
57 });
58 //lower.waste = min.diff;
59 },
60 /**
61 * Goes through and figures out which package has the greatest depth
62 */
63 maxDepth: function(shares){
64 var packageDepths = {},
65 max = 0;
66 shares.forEach(function(share){
67 share.bundles.forEach(function(appName){
68 packageDepths[appName] = (!packageDepths[appName] ?
69 1 : packageDepths[appName] +1);
70 max = Math.max(packageDepths[appName], max);
71 });
72 });
73 return max;
74 },
75 /**
76 * Goes through every combination of shares and returns the one with
77 * the smallest difference.
78 * Shares can have a waste property that has how much waste the share
79 * currently has accumulated.
80 * @param {{}} shares
81 * @return {min}
82 * {
83 * waste : 123213, // the amount of waste in the composite share
84 * lower : share, // the more base share, whos conents should
85 * be run first
86 * higher: share // the less base share, whos contents should
87 * run later
88 * }
89 */
90 min: function(shares, depth){
91 var min = {diff: {
92 totalWaste: Infinity,
93 }, lower: 0, higher: 0};
94 for(var i = 0; i < shares.length; i++){
95 var shareA = shares[i];
96 if( shareA.bundles.length == 1 ){
97 continue;
98 }
99 for(var j = i+1; j < shares.length; j++){
100 var shareB = shares[j],
101 diff;
102
103 var isTooSmall = shareB.bundles.length == 1 &&
104 depth !== 1;
105
106 if(isTooSmall){
107 continue;
108 }
109
110 diff = this.diff(shareA, shareB);
111
112 if(diff.totalWaste < min.diff.totalWaste){
113 min = {
114 diff : diff,
115 lower: i,
116 higher: j
117 };
118 }
119 }
120 }
121 return min.totalWaste === Infinity ? null : min;
122 },
123 /**
124 * returns a hash of the app names for quick checking
125 */
126 appsHash : function(shared){
127 var apps = {};
128 shared.bundles.forEach(function(name){
129 apps[name] = true;
130 });
131 return apps;
132 },
133 // return a difference between one share and another
134 // essentially, which apps will have the waste incured by loading
135 // b
136 diff: function(sharedA, sharedB){
137 // combine nodes
138 var nodes = sharedA.nodes.concat(sharedB.nodes),
139 // bundle names to their waste
140 diff = {bundleToWaste: {}, totalWaste: 0};
141
142
143 nodes.forEach(function(file){
144 file.bundles.forEach(function(appName){
145 diff.bundleToWaste[appName] = 0;
146 });
147 });
148 // For each app, check if the node is in it, if not add to its waste.
149 for(var appName in diff.bundleToWaste){
150 nodes.forEach(function(node){
151 // check file's appName
152 if(node.bundles.indexOf(appName) == -1){
153 diff.bundleToWaste[appName] += size(node);
154 }
155 });
156 diff.totalWaste += diff.bundleToWaste[appName];
157 }
158 return diff;
159 }
160 };
161
162module.exports = function(bundles, depth){
163 bundle.flatten(bundles, depth);
164};