Simplify HMR codepath

Reviewed By: davidaurelio

Differential Revision: D2839590

fb-gh-sync-id: 9bb14cafc69eec7d7a8712b60435e29f2ba48d3c
This commit is contained in:
Martín Bigio 2016-01-20 07:13:26 -08:00 committed by facebook-github-bot-7
parent 4bd39500f8
commit 4afeb4310b
7 changed files with 545 additions and 458 deletions

View File

@ -168,18 +168,18 @@ function attachHMRServer({httpServer, path, packagerServer}) {
entryFile: client.bundleEntry, entryFile: client.bundleEntry,
platform: client.platform, platform: client.platform,
modules: modulesToUpdate, modules: modulesToUpdate,
});
}) })
.then(update => { })
if (!client) { .then(bundle => {
if (!client || !bundle) {
return; return;
} }
// check we actually want to send an HMR update const hmrUpdate = bundle.getSource();
if (update) { if (hmrUpdate) {
return JSON.stringify({ return JSON.stringify({
type: 'update', type: 'update',
body: update, body: hmrUpdate,
}); });
} }
}) })
@ -206,15 +206,12 @@ function attachHMRServer({httpServer, path, packagerServer}) {
return JSON.stringify({type: 'error', body}); return JSON.stringify({type: 'error', body});
}) })
.then(bundle => { .then(update => {
if (!client) { if (!client) {
return; return;
} }
// check we actually want to send an HMR update client.ws.send(update);
if (bundle) {
client.ws.send(bundle);
}
}); });
}, },
() => { () => {

View File

@ -10,6 +10,7 @@
const _ = require('underscore'); const _ = require('underscore');
const base64VLQ = require('./base64-vlq'); const base64VLQ = require('./base64-vlq');
const BundleBase = require('./BundleBase');
const UglifyJS = require('uglify-js'); const UglifyJS = require('uglify-js');
const ModuleTransport = require('../lib/ModuleTransport'); const ModuleTransport = require('../lib/ModuleTransport');
const Activity = require('../Activity'); const Activity = require('../Activity');
@ -24,11 +25,9 @@ const getNameAndCode = ({name, code}) => ({name, code});
const getNameAndMinifiedCode = const getNameAndMinifiedCode =
({name, code}) => ({name, code: minifyCode(code)}); ({name, code}) => ({name, code: minifyCode(code)});
class Bundle { class Bundle extends BundleBase {
constructor(sourceMapUrl) { constructor(sourceMapUrl) {
this._finalized = false; super();
this._modules = [];
this._assets = [];
this._sourceMap = false; this._sourceMap = false;
this._sourceMapUrl = sourceMapUrl; this._sourceMapUrl = sourceMapUrl;
this._shouldCombineSourceMaps = false; this._shouldCombineSourceMaps = false;
@ -36,58 +35,49 @@ class Bundle {
this._numRequireCalls = 0; this._numRequireCalls = 0;
} }
setMainModuleId(moduleId) { addModule(resolver, response, module, transformed) {
this._mainModuleId = moduleId; return resolver.wrapModule(
} response,
module,
addModule(module) { transformed.code
if (!(module instanceof ModuleTransport)) { ).then(({code, name}) => {
throw new Error('Expeceted a ModuleTransport object'); const moduleTransport = new ModuleTransport({
} code,
name,
map: transformed.map,
sourceCode: transformed.sourceCode,
sourcePath: transformed.sourcePath,
virtual: transformed.virtual,
});
// If we get a map from the transformer we'll switch to a mode // If we get a map from the transformer we'll switch to a mode
// were we're combining the source maps as opposed to // were we're combining the source maps as opposed to
if (!this._shouldCombineSourceMaps && module.map != null) { if (!this._shouldCombineSourceMaps && moduleTransport.map != null) {
this._shouldCombineSourceMaps = true; this._shouldCombineSourceMaps = true;
} }
this._modules.push(module); super.addModule(moduleTransport);
} });
getModules() {
return this._modules;
}
getMainModuleId() {
return this._mainModuleId;
} }
setNumPrependedModules(n) { setNumPrependedModules(n) {
this._numPrependedModules = n; this._numPrependedModules = n;
} }
addAsset(asset) {
this._assets.push(asset);
}
finalize(options) { finalize(options) {
options = options || {}; options = options || {};
if (options.runMainModule) { if (options.runMainModule) {
options.runBeforeMainModule.forEach(this._addRequireCall, this); options.runBeforeMainModule.forEach(this._addRequireCall, this);
this._addRequireCall(this._mainModuleId); this._addRequireCall(super.getMainModuleId());
} }
Object.freeze(this._modules); super.finalize();
Object.seal(this._modules);
Object.freeze(this._assets);
Object.seal(this._assets);
this._finalized = true;
} }
_addRequireCall(moduleId) { _addRequireCall(moduleId) {
const code = ';require("' + moduleId + '");'; const code = ';require("' + moduleId + '");';
const name = 'require-' + moduleId; const name = 'require-' + moduleId;
this.addModule(new ModuleTransport({ super.addModule(new ModuleTransport({
name, name,
code, code,
virtual: true, virtual: true,
@ -97,21 +87,6 @@ class Bundle {
this._numRequireCalls += 1; this._numRequireCalls += 1;
} }
_assertFinalized() {
if (!this._finalized) {
throw new Error('Bundle needs to be finalized before getting any source');
}
}
_getSource(dev) {
if (this._source) {
return this._source;
}
this._source = _.pluck(this._modules, 'code').join('\n');
return this._source;
}
_getInlineSourceMap(dev) { _getInlineSourceMap(dev) {
if (this._inlineSourceMap == null) { if (this._inlineSourceMap == null) {
const sourceMap = this.getSourceMap({excludeSource: true, dev}); const sourceMap = this.getSourceMap({excludeSource: true, dev});
@ -123,7 +98,7 @@ class Bundle {
} }
getSource(options) { getSource(options) {
this._assertFinalized(); super.assertFinalized();
options = options || {}; options = options || {};
@ -131,7 +106,7 @@ class Bundle {
return this.getMinifiedSourceAndMap(options.dev).code; return this.getMinifiedSourceAndMap(options.dev).code;
} }
let source = this._getSource(options.dev); let source = super.getSource();
if (options.inlineSourceMap) { if (options.inlineSourceMap) {
source += SOURCEMAPPING_URL + this._getInlineSourceMap(options.dev); source += SOURCEMAPPING_URL + this._getInlineSourceMap(options.dev);
@ -143,7 +118,7 @@ class Bundle {
} }
getUnbundle({minify}) { getUnbundle({minify}) {
const allModules = this._modules.slice(); const allModules = super.getModules().slice();
const prependedModules = this._numPrependedModules; const prependedModules = this._numPrependedModules;
const requireCalls = this._numRequireCalls; const requireCalls = this._numRequireCalls;
@ -163,13 +138,13 @@ class Bundle {
} }
getMinifiedSourceAndMap(dev) { getMinifiedSourceAndMap(dev) {
this._assertFinalized(); super.assertFinalized();
if (this._minifiedSourceAndMap) { if (this._minifiedSourceAndMap) {
return this._minifiedSourceAndMap; return this._minifiedSourceAndMap;
} }
let source = this._getSource(dev); let source = this.getSource();
let map = this.getSourceMap(); let map = this.getSourceMap();
if (!dev) { if (!dev) {
@ -235,7 +210,7 @@ class Bundle {
}; };
let line = 0; let line = 0;
this._modules.forEach(function(module) { super.getModules().forEach(function(module) {
let map = module.map; let map = module.map;
if (module.virtual) { if (module.virtual) {
map = generateSourceMapForVirtualModule(module); map = generateSourceMapForVirtualModule(module);
@ -256,7 +231,7 @@ class Bundle {
} }
getSourceMap(options) { getSourceMap(options) {
this._assertFinalized(); super.assertFinalized();
options = options || {}; options = options || {};
@ -271,22 +246,18 @@ class Bundle {
const mappings = this._getMappings(); const mappings = this._getMappings();
const map = { const map = {
file: 'bundle.js', file: 'bundle.js',
sources: _.pluck(this._modules, 'sourcePath'), sources: _.pluck(super.getModules(), 'sourcePath'),
version: 3, version: 3,
names: [], names: [],
mappings: mappings, mappings: mappings,
sourcesContent: options.excludeSource sourcesContent: options.excludeSource
? [] : _.pluck(this._modules, 'sourceCode') ? [] : _.pluck(super.getModules(), 'sourceCode')
}; };
return map; return map;
} }
getAssets() {
return this._assets;
}
_getMappings() { _getMappings() {
const modules = this._modules; const modules = super.getModules();
// The first line mapping in our package is basically the base64vlq code for // The first line mapping in our package is basically the base64vlq code for
// zeros (A). // zeros (A).
@ -333,7 +304,7 @@ class Bundle {
} }
getJSModulePaths() { getJSModulePaths() {
return this._modules.filter(function(module) { return super.getModules().filter(function(module) {
// Filter out non-js files. Like images etc. // Filter out non-js files. Like images etc.
return !module.virtual; return !module.virtual;
}).map(function(module) { }).map(function(module) {
@ -343,7 +314,7 @@ class Bundle {
getDebugInfo() { getDebugInfo() {
return [ return [
'<div><h3>Main Module:</h3> ' + this._mainModuleId + '</div>', '<div><h3>Main Module:</h3> ' + super.getMainModuleId() + '</div>',
'<style>', '<style>',
'pre.collapsed {', 'pre.collapsed {',
' height: 10px;', ' height: 10px;',
@ -355,7 +326,7 @@ class Bundle {
'}', '}',
'</style>', '</style>',
'<h3> Module paths and transformed code: </h3>', '<h3> Module paths and transformed code: </h3>',
this._modules.map(function(m) { super.getModules().map(function(m) {
return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' + return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' +
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' + '<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' +
_.escape(m.code) + '</pre></code></div>'; _.escape(m.code) + '</pre></code></div>';
@ -369,10 +340,9 @@ class Bundle {
} }
return { return {
modules: this._modules, ...super.toJSON(),
assets: this._assets,
sourceMapUrl: this._sourceMapUrl, sourceMapUrl: this._sourceMapUrl,
mainModuleId: this._mainModuleId, mainModuleId: super.getMainModuleId(),
numPrependedModules: this._numPrependedModules, numPrependedModules: this._numPrependedModules,
numRequireCalls: this._numRequireCalls, numRequireCalls: this._numRequireCalls,
}; };
@ -380,18 +350,12 @@ class Bundle {
static fromJSON(json) { static fromJSON(json) {
const bundle = new Bundle(json.sourceMapUrl); const bundle = new Bundle(json.sourceMapUrl);
bundle._mainModuleId = json.mainModuleId;
bundle._assets = json.assets;
bundle._modules = json.modules;
bundle._sourceMapUrl = json.sourceMapUrl; bundle._sourceMapUrl = json.sourceMapUrl;
bundle._numPrependedModules = json.numPrependedModules; bundle._numPrependedModules = json.numPrependedModules;
bundle._numRequireCalls = json.numRequireCalls; bundle._numRequireCalls = json.numRequireCalls;
Object.freeze(bundle._modules); BundleBase.fromJSON(bundle, json);
Object.seal(bundle._modules);
Object.freeze(bundle._assets);
Object.seal(bundle._assets);
bundle._finalized = true;
return bundle; return bundle;
} }

View File

@ -0,0 +1,94 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const _ = require('underscore');
const ModuleTransport = require('../lib/ModuleTransport');
class BundleBase {
constructor() {
this._finalized = false;
this._modules = [];
this._assets = [];
}
getMainModuleId() {
return this._mainModuleId;
}
setMainModuleId(moduleId) {
this._mainModuleId = moduleId;
}
addModule(module) {
if (!module instanceof ModuleTransport) {
throw new Error('Expeceted a ModuleTransport object');
}
this._modules.push(module);
}
getModules() {
return this._modules;
}
getAssets() {
return this._assets;
}
addAsset(asset) {
this._assets.push(asset);
}
finalize(options) {
Object.freeze(this._modules);
Object.seal(this._modules);
Object.freeze(this._assets);
Object.seal(this._assets);
this._finalized = true;
}
getSource(options) {
this.assertFinalized();
if (this._source) {
return this._source;
}
this._source = _.pluck(this._modules, 'code').join('\n');
return this._source;
}
assertFinalized() {
if (!this._finalized) {
throw new Error('Bundle needs to be finalized before getting any source');
}
}
toJSON() {
return {
modules: this._modules,
assets: this._assets,
};
}
static fromJSON(bundle, json) {
bundle._assets = json.assets;
bundle._modules = json.modules;
bundle._mainModuleId = json.mainModuleId;
Object.freeze(bundle._modules);
Object.seal(bundle._modules);
Object.freeze(bundle._assets);
Object.seal(bundle._assets);
bundle._finalized = true;
}
}
module.exports = BundleBase;

View File

@ -0,0 +1,47 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
const BundleBase = require('./BundleBase');
const ModuleTransport = require('../lib/ModuleTransport');
class HMRBundle extends BundleBase {
constructor() {
super();
}
addModule(resolver, response, module, transformed) {
return resolver.resolveRequires(response,
module,
transformed.code,
).then(({name, code}) => {
code = `
__accept(
'${name}',
function(global, require, module, exports) {
${code}
}
);
`;
const moduleTransport = new ModuleTransport({
code,
name,
map: transformed.map,
sourceCode: transformed.sourceCode,
sourcePath: transformed.sourcePath,
virtual: transformed.virtual,
});
super.addModule(moduleTransport);
});
}
}
module.exports = HMRBundle;

View File

@ -10,35 +10,39 @@
jest.autoMockOff(); jest.autoMockOff();
var SourceMapGenerator = require('source-map').SourceMapGenerator; const Bundle = require('../Bundle');
const ModuleTransport = require('../../lib/ModuleTransport');
const Promise = require('Promise');
const SourceMapGenerator = require('source-map').SourceMapGenerator;
const UglifyJS = require('uglify-js');
var Bundle = require('../Bundle'); describe('Bundle', () => {
var ModuleTransport = require('../../lib/ModuleTransport');
var UglifyJS = require('uglify-js');
describe('Bundle', function() {
var bundle; var bundle;
beforeEach(function() { beforeEach(() => {
bundle = new Bundle('test_url'); bundle = new Bundle('test_url');
bundle.getSourceMap = jest.genMockFn().mockImpl(function() { bundle.getSourceMap = jest.genMockFn().mockImpl(() => {
return 'test-source-map'; return 'test-source-map';
}); });
}); });
describe('source bundle', function() { describe('source bundle', () => {
it('should create a bundle and get the source', function() { pit('should create a bundle and get the source', () => {
bundle.addModule(new ModuleTransport({ return Promise.resolve().then(() => {
return addModule({
bundle,
code: 'transformed foo;', code: 'transformed foo;',
sourceCode: 'source foo', sourceCode: 'source foo',
sourcePath: 'foo path', sourcePath: 'foo path',
})); });
bundle.addModule(new ModuleTransport({ }).then(() => {
return addModule({
bundle,
code: 'transformed bar;', code: 'transformed bar;',
sourceCode: 'source bar', sourceCode: 'source bar',
sourcePath: 'bar path', sourcePath: 'bar path',
})); });
}).then(() => {
bundle.finalize({}); bundle.finalize({});
expect(bundle.getSource({dev: true})).toBe([ expect(bundle.getSource({dev: true})).toBe([
'transformed foo;', 'transformed foo;',
@ -46,40 +50,49 @@ describe('Bundle', function() {
'\/\/# sourceMappingURL=test_url' '\/\/# sourceMappingURL=test_url'
].join('\n')); ].join('\n'));
}); });
});
it('should be ok to leave out the source map url', function() { pit('should be ok to leave out the source map url', () => {
var p = new Bundle(); const otherBundle = new Bundle();
p.addModule(new ModuleTransport({ return Promise.resolve().then(() => {
return addModule({
bundle: otherBundle,
code: 'transformed foo;', code: 'transformed foo;',
sourceCode: 'source foo', sourceCode: 'source foo',
sourcePath: 'foo path', sourcePath: 'foo path',
})); });
p.addModule(new ModuleTransport({ }).then(() => {
return addModule({
bundle: otherBundle,
code: 'transformed bar;', code: 'transformed bar;',
sourceCode: 'source bar', sourceCode: 'source bar',
sourcePath: 'bar path', sourcePath: 'bar path',
})); });
}).then(() => {
p.finalize({}); otherBundle.finalize({});
expect(p.getSource({dev: true})).toBe([ expect(otherBundle.getSource({dev: true})).toBe([
'transformed foo;', 'transformed foo;',
'transformed bar;', 'transformed bar;',
].join('\n')); ].join('\n'));
}); });
});
it('should create a bundle and add run module code', function() { pit('should create a bundle and add run module code', () => {
bundle.addModule(new ModuleTransport({ return Promise.resolve().then(() => {
return addModule({
bundle,
code: 'transformed foo;', code: 'transformed foo;',
sourceCode: 'source foo', sourceCode: 'source foo',
sourcePath: 'foo path' sourcePath: 'foo path',
})); });
}).then(() => {
bundle.addModule(new ModuleTransport({ return addModule({
bundle,
code: 'transformed bar;', code: 'transformed bar;',
sourceCode: 'source bar', sourceCode: 'source bar',
sourcePath: 'bar path' sourcePath: 'bar path',
})); });
}).then(() => {
bundle.setMainModuleId('foo'); bundle.setMainModuleId('foo');
bundle.finalize({ bundle.finalize({
runBeforeMainModule: ['bar'], runBeforeMainModule: ['bar'],
@ -93,9 +106,10 @@ describe('Bundle', function() {
'\/\/# sourceMappingURL=test_url', '\/\/# sourceMappingURL=test_url',
].join('\n')); ].join('\n'));
}); });
});
it('should get minified source', function() { pit('should get minified source', () => {
var minified = { const minified = {
code: 'minified', code: 'minified',
map: 'map', map: 'map',
}; };
@ -104,20 +118,27 @@ describe('Bundle', function() {
return minified; return minified;
}; };
bundle.addModule(new ModuleTransport({ return Promise.resolve().then(() => {
return addModule({
bundle,
code: 'transformed foo;', code: 'transformed foo;',
sourceCode: 'source foo', sourceCode: 'source foo',
sourcePath: 'foo path' sourcePath: 'foo path',
})); });
}).then(() => {
bundle.finalize(); bundle.finalize();
expect(bundle.getMinifiedSourceAndMap({dev: true})).toBe(minified); expect(bundle.getMinifiedSourceAndMap({dev: true})).toBe(minified);
}); });
}); });
});
describe('sourcemap bundle', function() { describe('sourcemap bundle', () => {
it('should create sourcemap', function() { pit('should create sourcemap', () => {
var p = new Bundle('test_url'); const otherBundle = new Bundle('test_url');
p.addModule(new ModuleTransport({
return Promise.resolve().then(() => {
return addModule({
bundle: otherBundle,
code: [ code: [
'transformed foo', 'transformed foo',
'transformed foo', 'transformed foo',
@ -129,8 +150,10 @@ describe('Bundle', function() {
'source foo', 'source foo',
].join('\n'), ].join('\n'),
sourcePath: 'foo path', sourcePath: 'foo path',
})); });
p.addModule(new ModuleTransport({ }).then(() => {
return addModule({
bundle: otherBundle,
code: [ code: [
'transformed bar', 'transformed bar',
'transformed bar', 'transformed bar',
@ -142,49 +165,54 @@ describe('Bundle', function() {
'source bar', 'source bar',
].join('\n'), ].join('\n'),
sourcePath: 'bar path', sourcePath: 'bar path',
})); });
}).then(() => {
p.setMainModuleId('foo'); otherBundle.setMainModuleId('foo');
p.finalize({ otherBundle.finalize({
runBeforeMainModule: [], runBeforeMainModule: [],
runMainModule: true, runMainModule: true,
}); });
var s = p.getSourceMap({dev: true}); const sourceMap = otherBundle.getSourceMap({dev: true});
expect(s).toEqual(genSourceMap(p.getModules())); expect(sourceMap).toEqual(genSourceMap(otherBundle.getModules()));
});
}); });
it('should combine sourcemaps', function() { pit('should combine sourcemaps', () => {
var p = new Bundle('test_url'); const otherBundle = new Bundle('test_url');
p.addModule(new ModuleTransport({ return Promise.resolve().then(() => {
return addModule({
bundle: otherBundle,
code: 'transformed foo;\n', code: 'transformed foo;\n',
sourceCode: 'source foo',
map: {name: 'sourcemap foo'}, map: {name: 'sourcemap foo'},
sourceCode: 'source foo', sourcePath: 'foo path',
sourcePath: 'foo path' });
})); }).then(() => {
return addModule({
p.addModule(new ModuleTransport({ bundle: otherBundle,
code: 'transformed foo;\n', code: 'transformed bar;\n',
sourceCode: 'source bar',
map: {name: 'sourcemap bar'}, map: {name: 'sourcemap bar'},
sourceCode: 'source foo', sourcePath: 'bar path',
sourcePath: 'foo path' });
})); }).then(() => {
return addModule({
p.addModule(new ModuleTransport({ bundle: otherBundle,
code: 'image module;\nimage module;', code: 'image module;\nimage module;',
virtual: true, virtual: true,
sourceCode: 'image module;\nimage module;', sourceCode: 'image module;\nimage module;',
sourcePath: 'image.png', sourcePath: 'image.png',
})); });
}).then(() => {
p.setMainModuleId('foo'); otherBundle.setMainModuleId('foo');
p.finalize({ otherBundle.finalize({
runBeforeMainModule: ['InitializeJavaScriptAppEngine'], runBeforeMainModule: ['InitializeJavaScriptAppEngine'],
runMainModule: true, runMainModule: true,
}); });
var s = p.getSourceMap({dev: true}); const sourceMap = otherBundle.getSourceMap({dev: true});
expect(s).toEqual({ expect(sourceMap).toEqual({
file: 'bundle.js', file: 'bundle.js',
version: 3, version: 3,
sections: [ sections: [
@ -236,9 +264,10 @@ describe('Bundle', function() {
}); });
}); });
}); });
});
describe('getAssets()', function() { describe('getAssets()', () => {
it('should save and return asset objects', function() { it('should save and return asset objects', () => {
var p = new Bundle('test_url'); var p = new Bundle('test_url');
var asset1 = {}; var asset1 = {};
var asset2 = {}; var asset2 = {};
@ -249,22 +278,27 @@ describe('Bundle', function() {
}); });
}); });
describe('getJSModulePaths()', function() { describe('getJSModulePaths()', () => {
it('should return module paths', function() { pit('should return module paths', () => {
var p = new Bundle('test_url'); var otherBundle = new Bundle('test_url');
p.addModule(new ModuleTransport({ return Promise.resolve().then(() => {
return addModule({
bundle: otherBundle,
code: 'transformed foo;\n', code: 'transformed foo;\n',
sourceCode: 'source foo', sourceCode: 'source foo',
sourcePath: 'foo path' sourcePath: 'foo path',
})); });
p.addModule(new ModuleTransport({ }).then(() => {
return addModule({
bundle: otherBundle,
code: 'image module;\nimage module;', code: 'image module;\nimage module;',
virtual: true, virtual: true,
sourceCode: 'image module;\nimage module;', sourceCode: 'image module;\nimage module;',
sourcePath: 'image.png', sourcePath: 'image.png',
})); });
}).then(() => {
expect(p.getJSModulePaths()).toEqual(['foo path']); expect(otherBundle.getJSModulePaths()).toEqual(['foo path']);
});
}); });
}); });
}); });
@ -302,3 +336,20 @@ function genSourceMap(modules) {
} }
return sourceMapGen.toJSON(); return sourceMapGen.toJSON();
} }
function resolverFor(code) {
return {
wrapModule: (response, module, sourceCode) => Promise.resolve(
{name: 'name', code}
),
};
}
function addModule({bundle, code, sourceCode, sourcePath, map, virtual}) {
return bundle.addModule(
resolverFor(code),
null,
null,
{sourceCode, sourcePath, map, virtual}
);
}

View File

@ -161,46 +161,29 @@ describe('Bundler', function() {
runBeforeMainModule: [], runBeforeMainModule: [],
runModule: true, runModule: true,
sourceMapUrl: 'source_map_url', sourceMapUrl: 'source_map_url',
}).then(function(p) { }).then(bundle => {
expect(p.addModule.mock.calls[0][0]).toEqual({ const ithAddedModule = (i) => bundle.addModule.mock.calls[i][2].path;
name: 'foo',
code: 'lol transformed /root/foo.js lol',
map: 'sourcemap /root/foo.js',
sourceCode: 'source /root/foo.js',
sourcePath: '/root/foo.js',
});
expect(p.addModule.mock.calls[1][0]).toEqual({ expect(ithAddedModule(0)).toEqual('/root/foo.js');
name: 'bar', expect(ithAddedModule(1)).toEqual('/root/bar.js');
code: 'lol transformed /root/bar.js lol', expect(ithAddedModule(2)).toEqual('/root/img/img.png');
map: 'sourcemap /root/bar.js', expect(ithAddedModule(3)).toEqual('/root/img/new_image.png');
sourceCode: 'source /root/bar.js', expect(ithAddedModule(4)).toEqual('/root/file.json');
sourcePath: '/root/bar.js'
});
var imgModule_DEPRECATED = { expect(bundle.finalize.mock.calls[0]).toEqual([
{runMainModule: true, runBeforeMainModule: []}
]);
expect(bundle.addAsset.mock.calls).toContain([{
__packager_asset: true, __packager_asset: true,
path: '/root/img/img.png', path: '/root/img/img.png',
uri: 'img', uri: 'img',
width: 25, width: 25,
height: 50, height: 50,
deprecated: true, deprecated: true,
}; }]);
expect(p.addModule.mock.calls[2][0]).toEqual({ expect(bundle.addAsset.mock.calls).toContain([{
name: 'image!img',
code: 'lol module.exports = ' +
JSON.stringify(imgModule_DEPRECATED) +
'; lol',
sourceCode: 'module.exports = ' +
JSON.stringify(imgModule_DEPRECATED) +
';',
sourcePath: '/root/img/img.png',
virtual: true,
map: undefined,
});
var imgModule = {
__packager_asset: true, __packager_asset: true,
fileSystemLocation: '/root/img', fileSystemLocation: '/root/img',
httpServerLocation: '/assets/img', httpServerLocation: '/assets/img',
@ -215,41 +198,7 @@ describe('Bundler', function() {
hash: 'i am a hash', hash: 'i am a hash',
name: 'img', name: 'img',
type: 'png', type: 'png',
}; }]);
expect(p.addModule.mock.calls[3][0]).toEqual({
name: 'new_image.png',
code: 'lol module.exports = require("AssetRegistry").registerAsset(' +
JSON.stringify(imgModule) +
'); lol',
sourceCode: 'module.exports = require("AssetRegistry").registerAsset(' +
JSON.stringify(imgModule) +
');',
sourcePath: '/root/img/new_image.png',
virtual: true,
map: undefined,
});
expect(p.addModule.mock.calls[4][0]).toEqual({
name: 'package/file.json',
code: 'lol module.exports = {"json":true}; lol',
sourceCode: 'module.exports = {"json":true};',
sourcePath: '/root/file.json',
map: undefined,
virtual: true,
});
expect(p.finalize.mock.calls[0]).toEqual([
{runMainModule: true, runBeforeMainModule: []}
]);
expect(p.addAsset.mock.calls).toContain([
imgModule_DEPRECATED
]);
expect(p.addAsset.mock.calls).toContain([
imgModule
]);
// TODO(amasad) This fails with 0 != 5 in OSS // TODO(amasad) This fails with 0 != 5 in OSS
//expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length); //expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length);

View File

@ -18,6 +18,7 @@ const Cache = require('../DependencyResolver/Cache');
const Transformer = require('../JSTransformer'); const Transformer = require('../JSTransformer');
const Resolver = require('../Resolver'); const Resolver = require('../Resolver');
const Bundle = require('./Bundle'); const Bundle = require('./Bundle');
const HMRBundle = require('./HMRBundle');
const PrepackBundle = require('./PrepackBundle'); const PrepackBundle = require('./PrepackBundle');
const Activity = require('../Activity'); const Activity = require('../Activity');
const ModuleTransport = require('../lib/ModuleTransport'); const ModuleTransport = require('../lib/ModuleTransport');
@ -155,31 +156,54 @@ class Bundler {
return this._bundlesLayout.generateLayout(main, isDev); return this._bundlesLayout.generateLayout(main, isDev);
} }
bundle({ bundle(options) {
return this._bundle({
bundle: new Bundle(options.sourceMapUrl),
includeSystemDependencies: true,
...options,
});
}
bundleForHMR(options) {
return this._bundle({
bundle: new HMRBundle(),
hot: true,
...options,
});
}
_bundle({
bundle,
modules,
entryFile, entryFile,
runModule: runMainModule, runModule: runMainModule,
runBeforeMainModule, runBeforeMainModule,
sourceMapUrl,
dev: isDev, dev: isDev,
includeSystemDependencies,
platform, platform,
unbundle: isUnbundle, unbundle: isUnbundle,
hot: hot, hot: hot,
}) { }) {
// Const cannot have the same name as the method (babel/babel#2834)
const bbundle = new Bundle(sourceMapUrl);
const findEventId = Activity.startEvent('find dependencies'); const findEventId = Activity.startEvent('find dependencies');
let transformEventId; let transformEventId;
const moduleSystem = this._resolver.getModuleSystemDependencies(
{ dev: isDev, platform, isUnbundle }
);
return this.getDependencies(entryFile, isDev, platform).then((response) => { return this.getDependencies(entryFile, isDev, platform).then((response) => {
Activity.endEvent(findEventId); Activity.endEvent(findEventId);
bundle.setMainModuleId(response.mainModuleId);
transformEventId = Activity.startEvent('transform'); transformEventId = Activity.startEvent('transform');
// Prepend the module system polyfill to the top of dependencies const moduleSystemDeps = includeSystemDependencies
var dependencies = moduleSystem.concat(response.dependencies); ? this._resolver.getModuleSystemDependencies(
{ dev: isDev, platform, isUnbundle }
)
: [];
const modulesToProcess = modules || response.dependencies;
const dependencies = moduleSystemDeps.concat(modulesToProcess);
bundle.setNumPrependedModules && bundle.setNumPrependedModules(
response.numPrependedDependencies + moduleSystemDeps.length
);
let bar; let bar;
if (process.stdout.isTTY) { if (process.stdout.isTTY) {
@ -191,13 +215,11 @@ class Bundler {
}); });
} }
bbundle.setMainModuleId(response.mainModuleId);
bbundle.setNumPrependedModules(
response.numPrependedDependencies + moduleSystem.length);
return Promise.all( return Promise.all(
dependencies.map( dependencies.map(
module => this._transformModule( module => {
bbundle, return this._transformModule(
bundle,
response, response,
module, module,
platform, platform,
@ -206,19 +228,28 @@ class Bundler {
if (bar) { if (bar) {
bar.tick(); bar.tick();
} }
return this._wrapTransformedModule(response, module, transformed);
})
)
);
}).then((transformedModules) => {
Activity.endEvent(transformEventId);
transformedModules.forEach(function(moduleTransport) { return {
bbundle.addModule(moduleTransport); module,
transformed,
};
}); });
}
bbundle.finalize({runBeforeMainModule, runMainModule}); )
return bbundle; ).then(transformedModules => Promise.all(
transformedModules.map(({module, transformed}) => {
return bundle.addModule(
this._resolver,
response,
module,
transformed,
);
})
));
}).then(() => {
Activity.endEvent(transformEventId);
bundle.finalize({runBeforeMainModule, runMainModule});
return bundle;
}); });
} }
@ -284,35 +315,6 @@ class Bundler {
}); });
} }
bundleForHMR({entryFile, platform, modules}) {
return this.getDependencies(entryFile, /*isDev*/true, platform)
.then(response => {
return Promise.all(
modules.map(module => {
return Promise.all([
module.getName(),
this._transformModuleForHMR(module, platform),
]).then(([moduleName, transformed]) => {
return this._resolver.resolveRequires(response,
module,
transformed.code,
).then(({name, code}) => {
return (`
__accept(
'${moduleName}',
function(global, require, module, exports) {
${code}
}
);
`);
});
});
})
);
})
.then(modules => modules.join('\n'));
}
_transformModuleForHMR(module, platform) { _transformModuleForHMR(module, platform) {
if (module.isAsset()) { if (module.isAsset()) {
return this._generateAssetObjAndCode(module, platform).then( return this._generateAssetObjAndCode(module, platform).then(
@ -401,23 +403,6 @@ class Bundler {
} }
} }
_wrapTransformedModule(response, module, transformed) {
return this._resolver.wrapModule(
response,
module,
transformed.code
).then(
({code, name}) => new ModuleTransport({
code,
name,
map: transformed.map,
sourceCode: transformed.sourceCode,
sourcePath: transformed.sourcePath,
virtual: transformed.virtual,
})
);
}
getGraphDebugInfo() { getGraphDebugInfo() {
return this._resolver.getDebugInfo(); return this._resolver.getDebugInfo();
} }