From 978592faabfa324221cb981703da4da08844fc68 Mon Sep 17 00:00:00 2001 From: Ashok Menon Date: Thu, 27 Apr 2017 17:35:24 -0700 Subject: [PATCH] Order modules in bundle by path. Reviewed By: davidaurelio Differential Revision: D4835227 fbshipit-source-id: 4974036fed7452447501fc07445afaa349e521c9 --- local-cli/bundle/buildBundle.js | 1 + local-cli/core/index.js | 8 +- .../src/Bundler/__tests__/Bundler-test.js | 273 ++++++++++-------- packager/src/Bundler/index.js | 13 +- packager/src/Server/index.js | 3 + 5 files changed, 176 insertions(+), 122 deletions(-) diff --git a/local-cli/bundle/buildBundle.js b/local-cli/bundle/buildBundle.js index 26a1d386f02..4501f4dca6e 100644 --- a/local-cli/bundle/buildBundle.js +++ b/local-cli/bundle/buildBundle.js @@ -83,6 +83,7 @@ function buildBundle( globalTransformCache: null, hasteImpl: config.hasteImpl, platforms: defaultPlatforms.concat(platforms), + postProcessModules: config.postProcessModules, projectRoots: config.getProjectRoots(), providesModuleNodeModules: providesModuleNodeModules, resetCache: args.resetCache, diff --git a/local-cli/core/index.js b/local-cli/core/index.js index 6809c47e0a0..9396de3ec32 100644 --- a/local-cli/core/index.js +++ b/local-cli/core/index.js @@ -16,7 +16,7 @@ const defaultConfig = require('./default.config'); const minimist = require('minimist'); import type {GetTransformOptions} from '../../packager/src/Bundler'; -import type {HasteImpl} from '../../packager/src/node-haste/Module'; +import type Module, {HasteImpl} from '../../packager/src/node-haste/Module'; import type {CommandT} from '../commands'; /** @@ -68,6 +68,12 @@ export type ConfigT = { */ getDependencyConfig(pkgName: string): Object, + /** + * An optional function that can modify the module array before the bundle is + * finalized. + */ + postProcessModules?: (modules: Array, entryFile: string) => Array, + /** * A module that exports: * - a `getHasteName(filePath)` method that returns `hasteName` for module at diff --git a/packager/src/Bundler/__tests__/Bundler-test.js b/packager/src/Bundler/__tests__/Bundler-test.js index 8403abe3598..680ea8f31ae 100644 --- a/packager/src/Bundler/__tests__/Bundler-test.js +++ b/packager/src/Bundler/__tests__/Bundler-test.js @@ -149,121 +149,6 @@ describe('Bundler', function() { }); }); - it('create a bundle', function() { - assetServer.getAssetData.mockImplementation(() => { - return Promise.resolve({ - scales: [1, 2, 3], - files: [ - '/root/img/img.png', - '/root/img/img@2x.png', - '/root/img/img@3x.png', - ], - hash: 'i am a hash', - name: 'img', - type: 'png', - }); - }); - - return bundler.bundle({ - entryFile: '/root/foo.js', - runBeforeMainModule: [], - runModule: true, - sourceMapUrl: 'source_map_url', - }).then(bundle => { - const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path; - - expect(ithAddedModule(0)).toEqual('/root/foo.js'); - expect(ithAddedModule(1)).toEqual('/root/bar.js'); - expect(ithAddedModule(2)).toEqual('/root/img/new_image.png'); - expect(ithAddedModule(3)).toEqual('/root/file.json'); - - expect(bundle.finalize.mock.calls[0]).toEqual([{ - runModule: true, - runBeforeMainModule: [], - allowUpdates: false, - }]); - - expect(bundle.addAsset.mock.calls[0]).toEqual([{ - __packager_asset: true, - fileSystemLocation: '/root/img', - httpServerLocation: '/assets/img', - width: 50, - height: 100, - scales: [1, 2, 3], - files: [ - '/root/img/img.png', - '/root/img/img@2x.png', - '/root/img/img@3x.png', - ], - hash: 'i am a hash', - name: 'img', - type: 'png', - }]); - - // TODO(amasad) This fails with 0 != 5 in OSS - //expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length); - }); - }); - - it('loads and runs asset plugins', function() { - jest.mock('mockPlugin1', () => { - return asset => { - asset.extraReverseHash = asset.hash.split('').reverse().join(''); - return asset; - }; - }, {virtual: true}); - - jest.mock('asyncMockPlugin2', () => { - return asset => { - expect(asset.extraReverseHash).toBeDefined(); - return new Promise(resolve => { - asset.extraPixelCount = asset.width * asset.height; - resolve(asset); - }); - }; - }, {virtual: true}); - - const mockAsset = { - scales: [1, 2, 3], - files: [ - '/root/img/img.png', - '/root/img/img@2x.png', - '/root/img/img@3x.png', - ], - hash: 'i am a hash', - name: 'img', - type: 'png', - }; - assetServer.getAssetData.mockImplementation(() => Promise.resolve(mockAsset)); - - return bundler.bundle({ - entryFile: '/root/foo.js', - runBeforeMainModule: [], - runModule: true, - sourceMapUrl: 'source_map_url', - assetPlugins: ['mockPlugin1', 'asyncMockPlugin2'], - }).then(bundle => { - expect(bundle.addAsset.mock.calls[0]).toEqual([{ - __packager_asset: true, - fileSystemLocation: '/root/img', - httpServerLocation: '/assets/img', - width: 50, - height: 100, - scales: [1, 2, 3], - files: [ - '/root/img/img.png', - '/root/img/img@2x.png', - '/root/img/img@3x.png', - ], - hash: 'i am a hash', - name: 'img', - type: 'png', - extraReverseHash: 'hsah a ma i', - extraPixelCount: 5000, - }]); - }); - }); - it('gets the list of dependencies from the resolver', function() { const entryFile = '/root/foo.js'; return bundler.getDependencies({entryFile, recursive: true}).then(() => @@ -303,7 +188,163 @@ describe('Bundler', function() { expect(b._opts.platforms).toEqual(['android', 'vr']); }); - describe('getOrderedDependencyPaths', () => { + describe('.bundle', () => { + const mockAsset = { + scales: [1, 2, 3], + files: [ + '/root/img/img.png', + '/root/img/img@2x.png', + '/root/img/img@3x.png', + ], + hash: 'i am a hash', + name: 'img', + type: 'png', + }; + + beforeEach(() => { + assetServer.getAssetData + .mockImplementation(() => Promise.resolve(mockAsset)); + }); + + it('creates a bundle', function() { + return bundler.bundle({ + entryFile: '/root/foo.js', + runBeforeMainModule: [], + runModule: true, + sourceMapUrl: 'source_map_url', + }).then(bundle => { + const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path; + + expect(ithAddedModule(0)).toEqual('/root/foo.js'); + expect(ithAddedModule(1)).toEqual('/root/bar.js'); + expect(ithAddedModule(2)).toEqual('/root/img/new_image.png'); + expect(ithAddedModule(3)).toEqual('/root/file.json'); + + expect(bundle.finalize.mock.calls[0]).toEqual([{ + runModule: true, + runBeforeMainModule: [], + allowUpdates: false, + }]); + + expect(bundle.addAsset.mock.calls[0]).toEqual([{ + __packager_asset: true, + fileSystemLocation: '/root/img', + httpServerLocation: '/assets/img', + width: 50, + height: 100, + scales: [1, 2, 3], + files: [ + '/root/img/img.png', + '/root/img/img@2x.png', + '/root/img/img@3x.png', + ], + hash: 'i am a hash', + name: 'img', + type: 'png', + }]); + + // TODO(amasad) This fails with 0 != 5 in OSS + //expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length); + }); + }); + + it('loads and runs asset plugins', function() { + jest.mock('mockPlugin1', () => { + return asset => { + asset.extraReverseHash = asset.hash.split('').reverse().join(''); + return asset; + }; + }, {virtual: true}); + + jest.mock('asyncMockPlugin2', () => { + return asset => { + expect(asset.extraReverseHash).toBeDefined(); + return new Promise(resolve => { + asset.extraPixelCount = asset.width * asset.height; + resolve(asset); + }); + }; + }, {virtual: true}); + + return bundler.bundle({ + entryFile: '/root/foo.js', + runBeforeMainModule: [], + runModule: true, + sourceMapUrl: 'source_map_url', + assetPlugins: ['mockPlugin1', 'asyncMockPlugin2'], + }).then(bundle => { + expect(bundle.addAsset.mock.calls[0]).toEqual([{ + __packager_asset: true, + fileSystemLocation: '/root/img', + httpServerLocation: '/assets/img', + width: 50, + height: 100, + scales: [1, 2, 3], + files: [ + '/root/img/img.png', + '/root/img/img@2x.png', + '/root/img/img@3x.png', + ], + hash: 'i am a hash', + name: 'img', + type: 'png', + extraReverseHash: 'hsah a ma i', + extraPixelCount: 5000, + }]); + }); + }); + + it('calls the module post-processing function', () => { + const postProcessModules = jest.fn().mockImplementation((ms, e) => ms); + + const b = new Bundler({ + ...commonOptions, + postProcessModules, + projectRoots, + assetServer, + }); + + const entryFile = '/root/foo.js'; + return b.bundle({ + entryFile, + runBeforeMainModule: [], + runModule: true, + sourceMapUrl: 'source_map_url', + }).then(() => { + expect(postProcessModules).toBeCalledWith(modules, entryFile); + }); + }); + + it('respects the order of modules returned by the post-processing function', () => { + const postProcessModules = jest.fn().mockImplementation((ms, e) => ms.reverse()); + + const b = new Bundler({ + ...commonOptions, + postProcessModules, + projectRoots, + assetServer, + }); + + const entryFile = '/root/foo.js'; + return b.bundle({ + entryFile, + runBeforeMainModule: [], + runModule: true, + sourceMapUrl: 'source_map_url', + }).then(bundle => { + const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path; + + [ + '/root/file.json', + '/root/img/new_image.png', + '/root/bar.js', + '/root/foo.js', + ].forEach((path, ix) => expect(ithAddedModule(ix)).toEqual(path)); + }); + }); + }); + + describe('.getOrderedDependencyPaths', () => { beforeEach(() => { assetServer.getAssetData.mockImplementation(function(relPath) { if (relPath === 'img/new_image.png') { diff --git a/packager/src/Bundler/index.js b/packager/src/Bundler/index.js index 873bf0834ae..6ea0635c023 100644 --- a/packager/src/Bundler/index.js +++ b/packager/src/Bundler/index.js @@ -112,6 +112,7 @@ type Options = {| +hasteImpl?: HasteImpl, +platforms: Array, +polyfillModuleNames: Array, + +postProcessModules?: (modules: Array, entryFile: string) => Array, +projectRoots: Array, +providesModuleNodeModules?: Array, +reporter: Reporter, @@ -470,12 +471,14 @@ class Bundler { return {module, transformed}; }); - return Promise.all(response.dependencies.map(toModuleTransport)) + const deps = this._opts.postProcessModules == null + ? response.dependencies + : this._opts.postProcessModules(response.dependencies, entryFile); + + return Promise.all(deps.map(toModuleTransport)) .then(transformedModules => - Promise.resolve( - finalizeBundle({bundle, transformedModules, response, modulesByName}) - ).then(() => bundle) - ); + finalizeBundle({bundle, transformedModules, response, modulesByName}) + ).then(() => bundle); }); } diff --git a/packager/src/Server/index.js b/packager/src/Server/index.js index 8faa6bb94a7..82c697d4399 100644 --- a/packager/src/Server/index.js +++ b/packager/src/Server/index.js @@ -68,6 +68,7 @@ type Options = { moduleFormat?: string, platforms?: Array, polyfillModuleNames?: Array, + postProcessModules?: (modules: Array, entryFile: string) => Array, projectRoots: Array, providesModuleNodeModules?: Array, reporter: Reporter, @@ -139,6 +140,7 @@ class Server { moduleFormat: string, platforms: Array, polyfillModuleNames: Array, + postProcessModules?: (modules: Array, entryFile: string) => Array, projectRoots: Array, providesModuleNodeModules?: Array, reporter: Reporter, @@ -174,6 +176,7 @@ class Server { moduleFormat: options.moduleFormat != null ? options.moduleFormat : 'haste', platforms: options.platforms || defaults.platforms, polyfillModuleNames: options.polyfillModuleNames || [], + postProcessModules: options.postProcessModules, projectRoots: options.projectRoots, providesModuleNodeModules: options.providesModuleNodeModules, reporter: options.reporter,