UNPKG

11.9 kBJavaScriptView Raw
1'use strict';
2
3function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
4
5const Path = require('path');
6const crypto = require('crypto');
7
8/**
9 * A Bundle represents an output file, containing multiple assets. Bundles can have
10 * child bundles, which are bundles that are loaded dynamically from this bundle.
11 * Child bundles are also produced when importing an asset of a different type from
12 * the bundle, e.g. importing a CSS file from JS.
13 */
14class Bundle {
15 constructor(type, name, parent) {
16 this.type = type;
17 this.name = name;
18 this.parentBundle = parent;
19 this.entryAsset = null;
20 this.assets = new Set();
21 this.childBundles = new Set();
22 this.siblingBundles = new Set();
23 this.siblingBundlesMap = new Map();
24 this.offsets = new Map();
25 this.totalSize = 0;
26 this.bundleTime = 0;
27 }
28
29 static createWithAsset(asset, parentBundle) {
30 let bundle = new Bundle(asset.type, Path.join(asset.options.outDir, asset.generateBundleName()), parentBundle);
31
32 bundle.entryAsset = asset;
33 bundle.addAsset(asset);
34 return bundle;
35 }
36
37 addAsset(asset) {
38 asset.bundles.add(this);
39 this.assets.add(asset);
40 }
41
42 removeAsset(asset) {
43 asset.bundles.delete(this);
44 this.assets.delete(asset);
45 }
46
47 addOffset(asset, line) {
48 this.offsets.set(asset, line);
49 }
50
51 getOffset(asset) {
52 return this.offsets.get(asset) || 0;
53 }
54
55 getSiblingBundle(type) {
56 if (!type || type === this.type) {
57 return this;
58 }
59
60 if (!this.siblingBundlesMap.has(type)) {
61 let bundle = new Bundle(type, Path.join(Path.dirname(this.name), Path.basename(this.name, Path.extname(this.name)) + '.' + type), this);
62
63 this.childBundles.add(bundle);
64 this.siblingBundles.add(bundle);
65 this.siblingBundlesMap.set(type, bundle);
66 }
67
68 return this.siblingBundlesMap.get(type);
69 }
70
71 createChildBundle(entryAsset) {
72 let bundle = Bundle.createWithAsset(entryAsset, this);
73 this.childBundles.add(bundle);
74 return bundle;
75 }
76
77 createSiblingBundle(entryAsset) {
78 let bundle = this.createChildBundle(entryAsset);
79 this.siblingBundles.add(bundle);
80 return bundle;
81 }
82
83 get isEmpty() {
84 return this.assets.size === 0;
85 }
86
87 getBundleNameMap(contentHash, hashes = new Map()) {
88 let hashedName = this.getHashedBundleName(contentHash);
89 hashes.set(Path.basename(this.name), hashedName);
90 this.name = Path.join(Path.dirname(this.name), hashedName);
91
92 var _iteratorNormalCompletion = true;
93 var _didIteratorError = false;
94 var _iteratorError = undefined;
95
96 try {
97 for (var _iterator = this.childBundles.values()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
98 let child = _step.value;
99
100 child.getBundleNameMap(contentHash, hashes);
101 }
102 } catch (err) {
103 _didIteratorError = true;
104 _iteratorError = err;
105 } finally {
106 try {
107 if (!_iteratorNormalCompletion && _iterator.return) {
108 _iterator.return();
109 }
110 } finally {
111 if (_didIteratorError) {
112 throw _iteratorError;
113 }
114 }
115 }
116
117 return hashes;
118 }
119
120 getHashedBundleName(contentHash) {
121 // If content hashing is enabled, generate a hash from all assets in the bundle.
122 // Otherwise, use a hash of the filename so it remains consistent across builds.
123 let ext = Path.extname(this.name);
124 let hash = (contentHash ? this.getHash() : Path.basename(this.name, ext)).slice(-8);
125 let entryAsset = this.entryAsset || this.parentBundle.entryAsset;
126 let name = Path.basename(entryAsset.name, Path.extname(entryAsset.name));
127 let isMainEntry = entryAsset.name === entryAsset.options.mainFile;
128 let isEntry = isMainEntry || Array.from(entryAsset.parentDeps).some(dep => dep.entry);
129
130 // If this is the main entry file, use the output file option as the name if provided.
131 if (isMainEntry && entryAsset.options.outFile) {
132 name = entryAsset.options.outFile;
133 }
134
135 // If this is an entry asset, don't hash. Return a relative path
136 // from the main file so we keep the original file paths.
137 if (isEntry) {
138 return Path.join(Path.relative(Path.dirname(entryAsset.options.mainFile), Path.dirname(entryAsset.name)), name + ext);
139 }
140
141 // If this is an index file, use the parent directory name instead
142 // which is probably more descriptive.
143 if (name === 'index') {
144 name = Path.basename(Path.dirname(entryAsset.name));
145 }
146
147 // Add the content hash and extension.
148 return name + '.' + hash + ext;
149 }
150
151 package(bundler, oldHashes, newHashes = new Map()) {
152 var _this = this;
153
154 return _asyncToGenerator(function* () {
155 if (_this.isEmpty) {
156 return newHashes;
157 }
158
159 let hash = _this.getHash();
160 newHashes.set(_this.name, hash);
161
162 let promises = [];
163 let mappings = [];
164 if (!oldHashes || oldHashes.get(_this.name) !== hash) {
165 promises.push(_this._package(bundler));
166 }
167
168 var _iteratorNormalCompletion2 = true;
169 var _didIteratorError2 = false;
170 var _iteratorError2 = undefined;
171
172 try {
173 for (var _iterator2 = _this.childBundles.values()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
174 let bundle = _step2.value;
175
176 if (bundle.type === 'map') {
177 mappings.push(bundle);
178 } else {
179 promises.push(bundle.package(bundler, oldHashes, newHashes));
180 }
181 }
182 } catch (err) {
183 _didIteratorError2 = true;
184 _iteratorError2 = err;
185 } finally {
186 try {
187 if (!_iteratorNormalCompletion2 && _iterator2.return) {
188 _iterator2.return();
189 }
190 } finally {
191 if (_didIteratorError2) {
192 throw _iteratorError2;
193 }
194 }
195 }
196
197 yield Promise.all(promises);
198 var _iteratorNormalCompletion3 = true;
199 var _didIteratorError3 = false;
200 var _iteratorError3 = undefined;
201
202 try {
203 for (var _iterator3 = mappings[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
204 let bundle = _step3.value;
205
206 yield bundle.package(bundler, oldHashes, newHashes);
207 }
208 } catch (err) {
209 _didIteratorError3 = true;
210 _iteratorError3 = err;
211 } finally {
212 try {
213 if (!_iteratorNormalCompletion3 && _iterator3.return) {
214 _iterator3.return();
215 }
216 } finally {
217 if (_didIteratorError3) {
218 throw _iteratorError3;
219 }
220 }
221 }
222
223 return newHashes;
224 })();
225 }
226
227 _package(bundler) {
228 var _this2 = this;
229
230 return _asyncToGenerator(function* () {
231 let Packager = bundler.packagers.get(_this2.type);
232 let packager = new Packager(_this2, bundler);
233
234 let startTime = Date.now();
235 yield packager.setup();
236 yield packager.start();
237
238 let included = new Set();
239 var _iteratorNormalCompletion4 = true;
240 var _didIteratorError4 = false;
241 var _iteratorError4 = undefined;
242
243 try {
244 for (var _iterator4 = _this2.assets[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
245 let asset = _step4.value;
246
247 yield _this2._addDeps(asset, packager, included);
248 }
249 } catch (err) {
250 _didIteratorError4 = true;
251 _iteratorError4 = err;
252 } finally {
253 try {
254 if (!_iteratorNormalCompletion4 && _iterator4.return) {
255 _iterator4.return();
256 }
257 } finally {
258 if (_didIteratorError4) {
259 throw _iteratorError4;
260 }
261 }
262 }
263
264 yield packager.end();
265
266 _this2.bundleTime = Date.now() - startTime;
267 var _iteratorNormalCompletion5 = true;
268 var _didIteratorError5 = false;
269 var _iteratorError5 = undefined;
270
271 try {
272 for (var _iterator5 = _this2.assets[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
273 let asset = _step5.value;
274
275 _this2.bundleTime += asset.buildTime;
276 }
277 } catch (err) {
278 _didIteratorError5 = true;
279 _iteratorError5 = err;
280 } finally {
281 try {
282 if (!_iteratorNormalCompletion5 && _iterator5.return) {
283 _iterator5.return();
284 }
285 } finally {
286 if (_didIteratorError5) {
287 throw _iteratorError5;
288 }
289 }
290 }
291 })();
292 }
293
294 _addDeps(asset, packager, included) {
295 var _this3 = this;
296
297 return _asyncToGenerator(function* () {
298 if (!_this3.assets.has(asset) || included.has(asset)) {
299 return;
300 }
301
302 included.add(asset);
303
304 var _iteratorNormalCompletion6 = true;
305 var _didIteratorError6 = false;
306 var _iteratorError6 = undefined;
307
308 try {
309 for (var _iterator6 = asset.depAssets.values()[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
310 let depAsset = _step6.value;
311
312 yield _this3._addDeps(depAsset, packager, included);
313 }
314 } catch (err) {
315 _didIteratorError6 = true;
316 _iteratorError6 = err;
317 } finally {
318 try {
319 if (!_iteratorNormalCompletion6 && _iterator6.return) {
320 _iterator6.return();
321 }
322 } finally {
323 if (_didIteratorError6) {
324 throw _iteratorError6;
325 }
326 }
327 }
328
329 yield packager.addAsset(asset);
330 _this3.addAssetSize(asset, packager.getSize() - _this3.totalSize);
331 })();
332 }
333
334 addAssetSize(asset, size) {
335 asset.bundledSize = size;
336 this.totalSize += size;
337 }
338
339 getParents() {
340 let parents = [];
341 let bundle = this;
342
343 while (bundle) {
344 parents.push(bundle);
345 bundle = bundle.parentBundle;
346 }
347
348 return parents;
349 }
350
351 findCommonAncestor(bundle) {
352 // Get a list of parent bundles going up to the root
353 let ourParents = this.getParents();
354 let theirParents = bundle.getParents();
355
356 // Start from the root bundle, and find the first bundle that's different
357 let a = ourParents.pop();
358 let b = theirParents.pop();
359 let last;
360 while (a === b && ourParents.length > 0 && theirParents.length > 0) {
361 last = a;
362 a = ourParents.pop();
363 b = theirParents.pop();
364 }
365
366 if (a === b) {
367 // One bundle descended from the other
368 return a;
369 }
370
371 return last;
372 }
373
374 getHash() {
375 let hash = crypto.createHash('md5');
376 var _iteratorNormalCompletion7 = true;
377 var _didIteratorError7 = false;
378 var _iteratorError7 = undefined;
379
380 try {
381 for (var _iterator7 = this.assets[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
382 let asset = _step7.value;
383
384 hash.update(asset.hash);
385 }
386 } catch (err) {
387 _didIteratorError7 = true;
388 _iteratorError7 = err;
389 } finally {
390 try {
391 if (!_iteratorNormalCompletion7 && _iterator7.return) {
392 _iterator7.return();
393 }
394 } finally {
395 if (_didIteratorError7) {
396 throw _iteratorError7;
397 }
398 }
399 }
400
401 return hash.digest('hex');
402 }
403}
404
405module.exports = Bundle;
\No newline at end of file