Order modules in bundle by path.

Reviewed By: davidaurelio

Differential Revision: D4835227

fbshipit-source-id: 4974036fed7452447501fc07445afaa349e521c9
This commit is contained in:
Ashok Menon 2017-04-27 17:35:24 -07:00 committed by Facebook Github Bot
parent a7009077ed
commit 978592faab
5 changed files with 176 additions and 122 deletions

View File

@ -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,

View File

@ -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<Module>, entryFile: string) => Array<Module>,
/**
* A module that exports:
* - a `getHasteName(filePath)` method that returns `hasteName` for module at

View File

@ -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') {

View File

@ -112,6 +112,7 @@ type Options = {|
+hasteImpl?: HasteImpl,
+platforms: Array<string>,
+polyfillModuleNames: Array<string>,
+postProcessModules?: (modules: Array<Module>, entryFile: string) => Array<Module>,
+projectRoots: Array<string>,
+providesModuleNodeModules?: Array<string>,
+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);
});
}

View File

@ -68,6 +68,7 @@ type Options = {
moduleFormat?: string,
platforms?: Array<string>,
polyfillModuleNames?: Array<string>,
postProcessModules?: (modules: Array<Module>, entryFile: string) => Array<Module>,
projectRoots: Array<string>,
providesModuleNodeModules?: Array<string>,
reporter: Reporter,
@ -139,6 +140,7 @@ class Server {
moduleFormat: string,
platforms: Array<string>,
polyfillModuleNames: Array<string>,
postProcessModules?: (modules: Array<Module>, entryFile: string) => Array<Module>,
projectRoots: Array<string>,
providesModuleNodeModules?: Array<string>,
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,