diff --git a/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js b/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js index 2531b1151a..3a792a4467 100644 --- a/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js +++ b/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayout-test.js @@ -29,8 +29,15 @@ describe('BundlesLayout', () => { }); } + function isPolyfill() { + return false; + } + function dep(path) { - return {path}; + return { + path: path, + isPolyfill: isPolyfill, + }; } pit('should bundle sync dependencies', () => { @@ -52,9 +59,11 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(bundles).toEqual([ - [dep('/root/index.js'), dep('/root/a.js')], - ]) + expect(bundles).toEqual({ + id: 'bundle.0', + modules: [dep('/root/index.js'), dep('/root/a.js')], + children: [], + }) ); }); @@ -77,10 +86,15 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(bundles).toEqual([ - [dep('/root/index.js')], - [dep('/root/a.js')], - ]) + expect(bundles).toEqual({ + id: 'bundle.0', + modules: [dep('/root/index.js')], + children: [{ + id:'bundle.0.1', + modules: [dep('/root/a.js')], + children: [], + }], + }) ); }); @@ -108,11 +122,19 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(bundles).toEqual([ - [dep('/root/index.js')], - [dep('/root/a.js')], - [dep('/root/b.js')], - ]) + expect(bundles).toEqual({ + id: 'bundle.0', + modules: [dep('/root/index.js')], + children: [{ + id: 'bundle.0.1', + modules: [dep('/root/a.js')], + children: [{ + id: 'bundle.0.1.2', + modules: [dep('/root/b.js')], + children: [], + }], + }], + }) ); }); @@ -140,10 +162,15 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(bundles).toEqual([ - [dep('/root/index.js')], - [dep('/root/a.js'), dep('/root/b.js')], - ]) + expect(bundles).toEqual({ + id: 'bundle.0', + modules: [dep('/root/index.js')], + children: [{ + id: 'bundle.0.1', + modules: [dep('/root/a.js'), dep('/root/b.js')], + children: [], + }], + }) ); }); @@ -171,10 +198,15 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then( - bundles => expect(bundles).toEqual([ - [dep('/root/index.js'), dep('/root/a.js')], - [dep('/root/b.js')], - ]) + bundles => expect(bundles).toEqual({ + id: 'bundle.0', + modules: [dep('/root/index.js'), dep('/root/a.js')], + children: [{ + id: 'bundle.0.1', + modules: [dep('/root/b.js')], + children: [], + }], + }) ); }); @@ -184,7 +216,7 @@ describe('BundlesLayout', () => { case '/root/index.js': return Promise.resolve({ dependencies: [dep('/root/index.js'), dep('/root/a.js')], - asyncDependencies: [['/root/b.js']], + asyncDependencies: [['/root/b.js'], ['/root/c.js']], }); case '/root/a.js': return Promise.resolve({ @@ -194,13 +226,18 @@ describe('BundlesLayout', () => { case '/root/b.js': return Promise.resolve({ dependencies: [dep('/root/b.js')], - asyncDependencies: [['/root/c.js']], + asyncDependencies: [['/root/d.js']], }); case '/root/c.js': return Promise.resolve({ dependencies: [dep('/root/c.js')], asyncDependencies: [], }); + case '/root/d.js': + return Promise.resolve({ + dependencies: [dep('/root/d.js')], + asyncDependencies: [], + }); default: throw 'Undefined path: ' + path; } @@ -208,10 +245,11 @@ describe('BundlesLayout', () => { var layout = newBundlesLayout(); return layout.generateLayout(['/root/index.js']).then(() => { - expect(layout.getBundleIDForModule('/root/index.js')).toBe(0); - expect(layout.getBundleIDForModule('/root/a.js')).toBe(0); - expect(layout.getBundleIDForModule('/root/b.js')).toBe(1); - expect(layout.getBundleIDForModule('/root/c.js')).toBe(2); + expect(layout.getBundleIDForModule('/root/index.js')).toBe('bundle.0'); + expect(layout.getBundleIDForModule('/root/a.js')).toBe('bundle.0'); + expect(layout.getBundleIDForModule('/root/b.js')).toBe('bundle.0.1'); + expect(layout.getBundleIDForModule('/root/c.js')).toBe('bundle.0.2'); + expect(layout.getBundleIDForModule('/root/d.js')).toBe('bundle.0.1.3'); }); }); }); diff --git a/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js b/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js index 5ad4b523fa..9ff0f4354b 100644 --- a/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js +++ b/packager/react-packager/src/BundlesLayout/__tests__/BundlesLayoutIntegration-test.js @@ -78,15 +78,37 @@ describe('BundlesLayout', () => { return new BundlesLayout({dependencyResolver: resolver}); } - function modulePaths(bundles) { - if (!bundles) { - return null; - } + function stripPolyfills(bundle) { + return Promise + .all([ + Promise.all( + bundle.modules.map(module => module + .getName() + .then(name => [module, name]) + ), + ), + Promise.all( + bundle.children.map(childModule => stripPolyfills(childModule)), + ), + ]) + .then(([modules, children]) => { + modules = modules + .filter(([module, name]) => { // filter polyfills + for (let p of polyfills) { + if (name.indexOf(p) !== -1) { + return false; + } + } + return true; + }) + .map(([module, name]) => module.path); - return bundles.map( - bundle => bundle.filter(module => !module.isPolyfill()) - .map(module => module.path) - ); + return { + id: bundle.id, + modules: modules, + children: children, + }; + }); } function setMockFilesystem(mockFs) { @@ -104,10 +126,12 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - modulePaths(bundles).then(paths => - expect(paths).toEqual([ - ['/root/index.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [], + }) ) ); }); @@ -128,9 +152,13 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js', '/root/a.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js', '/root/a.js'], + children: [], + }) + ) ); }); @@ -150,10 +178,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/a.js'], + children: [], + }], + }) + ) ); }); @@ -178,11 +213,23 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js'], - ['/root/b.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [ + { + id: 'bundle.0.1', + modules: ['/root/a.js'], + children: [], + }, { + id: 'bundle.0.2', + modules: ['/root/b.js'], + children: [], + }, + ], + }) + ) ); }); @@ -206,10 +253,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/b.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/b.js'], + children: [], + }], + }) + ) ); }); @@ -234,10 +288,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js', '/root/a.js'], - ['/root/b.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js', '/root/a.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/b.js'], + children: [], + }], + }) + ) ); }); @@ -267,10 +328,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/b.js', '/root/c.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/b.js', '/root/c.js'], + children: [], + }], + }) + ) ); }); @@ -301,11 +369,24 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/c.js'], - ['/root/b.js', '/root/c.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [ + { + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/c.js'], + children: [], + }, + { + id: 'bundle.0.2', + modules: ['/root/b.js', '/root/c.js'], + children: [], + }, + ], + }) + ) ); }); @@ -335,11 +416,23 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js'], - ['/root/b.js', '/root/c.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [ + { + id: 'bundle.0.1', + modules: ['/root/a.js'], + children: [{ + id: 'bundle.0.1.2', + modules: ['/root/b.js', '/root/c.js'], + children: [], + }], + }, + ], + }) + ) ); }); @@ -369,10 +462,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/c.js', '/root/b.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/c.js', '/root/b.js'], + children: [], + }], + }) + ) ); }); @@ -394,10 +494,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/img.png'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/img.png'], + children: [], + }], + }) + ) ); }); @@ -425,11 +532,24 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/img.png'], - ['/root/b.js', '/root/img.png'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [ + { + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/img.png'], + children: [], + }, + { + id: 'bundle.0.2', + modules: ['/root/b.js', '/root/img.png'], + children: [], + }, + ], + }) + ) ); }); @@ -446,10 +566,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/img.png'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/img.png'], + children: [], + }], + }) + ) ); }); @@ -471,10 +598,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/a.js', '/root/img.png'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/a.js', '/root/img.png'], + children: [], + }], + }) + ) ); }); @@ -491,10 +625,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/img.png'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/img.png'], + children: [], + }], + }) + ) ); }); @@ -521,10 +662,17 @@ describe('BundlesLayout', () => { }); return newBundlesLayout().generateLayout(['/root/index.js']).then(bundles => - expect(modulePaths(bundles)).toEqual([ - ['/root/index.js'], - ['/root/aPackage/client.js'], - ]) + stripPolyfills(bundles).then(resolvedBundles => + expect(resolvedBundles).toEqual({ + id: 'bundle.0', + modules: ['/root/index.js'], + children: [{ + id: 'bundle.0.1', + modules: ['/root/aPackage/client.js'], + children: [], + }], + }) + ) ); }); }); diff --git a/packager/react-packager/src/BundlesLayout/index.js b/packager/react-packager/src/BundlesLayout/index.js index 507155549c..d7693a73da 100644 --- a/packager/react-packager/src/BundlesLayout/index.js +++ b/packager/react-packager/src/BundlesLayout/index.js @@ -18,6 +18,8 @@ const validateOpts = declareOpts({ }, }); +const BUNDLE_PREFIX = 'bundle'; + /** * Class that takes care of separating the graph of dependencies into * separate bundles @@ -31,16 +33,23 @@ class BundlesLayout { } generateLayout(entryPaths, isDev) { - const bundles = []; - var pending = [entryPaths]; + var currentBundleID = 0; + const rootBundle = { + id: BUNDLE_PREFIX + '.' + currentBundleID++, + modules: [], + children: [], + }; + var pending = [{paths: entryPaths, bundle: rootBundle}]; return promiseWhile( () => pending.length > 0, - () => bundles, + () => rootBundle, () => { + const {paths, bundle} = pending.shift(); + // pending sync dependencies we still need to explore for the current // pending dependency - let pendingSyncDeps = pending.shift(); + const pendingSyncDeps = paths; // accum variable for sync dependencies of the current pending // dependency we're processing @@ -51,22 +60,31 @@ class BundlesLayout { () => { const dependencies = _.values(syncDependencies); if (dependencies.length > 0) { - bundles.push(dependencies); + bundle.modules = dependencies; } }, - () => { + index => { const pendingSyncDep = pendingSyncDeps.shift(); return this._resolver .getDependencies(pendingSyncDep, {dev: isDev}) .then(deps => { deps.dependencies.forEach(dep => { - if (dep.path !== pendingSyncDep && !dep.isPolyfill) { + if (dep.path !== pendingSyncDep && !dep.isPolyfill()) { pendingSyncDeps.push(dep.path); } syncDependencies[dep.path] = dep; - this._moduleToBundle[dep.path] = bundles.length; + this._moduleToBundle[dep.path] = bundle.id; + }); + deps.asyncDependencies.forEach(asyncDeps => { + const childBundle = { + id: bundle.id + '.' + currentBundleID++, + modules: [], + children: [], + }; + + bundle.children.push(childBundle); + pending.push({paths: asyncDeps, bundle: childBundle}); }); - pending = pending.concat(deps.asyncDependencies); }); }, ); @@ -83,11 +101,17 @@ class BundlesLayout { // Once it's not satisfied anymore, it returns what the results callback // indicates function promiseWhile(condition, result, body) { + return _promiseWhile(condition, result, body, 0); +} + +function _promiseWhile(condition, result, body, index) { if (!condition()) { return Promise.resolve(result()); } - return body().then(() => promiseWhile(condition, result, body)); + return body(index).then(() => + _promiseWhile(condition, result, body, index + 1) + ); } module.exports = BundlesLayout;