diff --git a/react-packager/src/Bundler/Bundle.js b/react-packager/src/Bundler/Bundle.js index b5e591c8..04a2e1ae 100644 --- a/react-packager/src/Bundler/Bundle.js +++ b/react-packager/src/Bundler/Bundle.js @@ -10,6 +10,7 @@ const _ = require('underscore'); const base64VLQ = require('./base64-vlq'); +const BundleBase = require('./BundleBase'); const UglifyJS = require('uglify-js'); const ModuleTransport = require('../lib/ModuleTransport'); const Activity = require('../Activity'); @@ -24,11 +25,9 @@ const getNameAndCode = ({name, code}) => ({name, code}); const getNameAndMinifiedCode = ({name, code}) => ({name, code: minifyCode(code)}); -class Bundle { +class Bundle extends BundleBase { constructor(sourceMapUrl) { - this._finalized = false; - this._modules = []; - this._assets = []; + super(); this._sourceMap = false; this._sourceMapUrl = sourceMapUrl; this._shouldCombineSourceMaps = false; @@ -36,58 +35,49 @@ class Bundle { this._numRequireCalls = 0; } - setMainModuleId(moduleId) { - this._mainModuleId = moduleId; - } + addModule(resolver, response, module, transformed) { + return resolver.wrapModule( + response, + module, + transformed.code + ).then(({code, name}) => { + const moduleTransport = new ModuleTransport({ + code, + name, + map: transformed.map, + sourceCode: transformed.sourceCode, + sourcePath: transformed.sourcePath, + virtual: transformed.virtual, + }); - addModule(module) { - if (!(module instanceof ModuleTransport)) { - throw new Error('Expeceted a ModuleTransport object'); - } + // If we get a map from the transformer we'll switch to a mode + // were we're combining the source maps as opposed to + if (!this._shouldCombineSourceMaps && moduleTransport.map != null) { + this._shouldCombineSourceMaps = true; + } - // If we get a map from the transformer we'll switch to a mode - // were we're combining the source maps as opposed to - if (!this._shouldCombineSourceMaps && module.map != null) { - this._shouldCombineSourceMaps = true; - } - - this._modules.push(module); - } - - getModules() { - return this._modules; - } - - getMainModuleId() { - return this._mainModuleId; + super.addModule(moduleTransport); + }); } setNumPrependedModules(n) { this._numPrependedModules = n; } - addAsset(asset) { - this._assets.push(asset); - } - finalize(options) { options = options || {}; if (options.runMainModule) { options.runBeforeMainModule.forEach(this._addRequireCall, this); - this._addRequireCall(this._mainModuleId); + this._addRequireCall(super.getMainModuleId()); } - Object.freeze(this._modules); - Object.seal(this._modules); - Object.freeze(this._assets); - Object.seal(this._assets); - this._finalized = true; + super.finalize(); } _addRequireCall(moduleId) { const code = ';require("' + moduleId + '");'; const name = 'require-' + moduleId; - this.addModule(new ModuleTransport({ + super.addModule(new ModuleTransport({ name, code, virtual: true, @@ -97,21 +87,6 @@ class Bundle { 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) { if (this._inlineSourceMap == null) { const sourceMap = this.getSourceMap({excludeSource: true, dev}); @@ -123,7 +98,7 @@ class Bundle { } getSource(options) { - this._assertFinalized(); + super.assertFinalized(); options = options || {}; @@ -131,7 +106,7 @@ class Bundle { return this.getMinifiedSourceAndMap(options.dev).code; } - let source = this._getSource(options.dev); + let source = super.getSource(); if (options.inlineSourceMap) { source += SOURCEMAPPING_URL + this._getInlineSourceMap(options.dev); @@ -143,7 +118,7 @@ class Bundle { } getUnbundle({minify}) { - const allModules = this._modules.slice(); + const allModules = super.getModules().slice(); const prependedModules = this._numPrependedModules; const requireCalls = this._numRequireCalls; @@ -163,13 +138,13 @@ class Bundle { } getMinifiedSourceAndMap(dev) { - this._assertFinalized(); + super.assertFinalized(); if (this._minifiedSourceAndMap) { return this._minifiedSourceAndMap; } - let source = this._getSource(dev); + let source = this.getSource(); let map = this.getSourceMap(); if (!dev) { @@ -235,7 +210,7 @@ class Bundle { }; let line = 0; - this._modules.forEach(function(module) { + super.getModules().forEach(function(module) { let map = module.map; if (module.virtual) { map = generateSourceMapForVirtualModule(module); @@ -256,7 +231,7 @@ class Bundle { } getSourceMap(options) { - this._assertFinalized(); + super.assertFinalized(); options = options || {}; @@ -271,22 +246,18 @@ class Bundle { const mappings = this._getMappings(); const map = { file: 'bundle.js', - sources: _.pluck(this._modules, 'sourcePath'), + sources: _.pluck(super.getModules(), 'sourcePath'), version: 3, names: [], mappings: mappings, sourcesContent: options.excludeSource - ? [] : _.pluck(this._modules, 'sourceCode') + ? [] : _.pluck(super.getModules(), 'sourceCode') }; return map; } - getAssets() { - return this._assets; - } - _getMappings() { - const modules = this._modules; + const modules = super.getModules(); // The first line mapping in our package is basically the base64vlq code for // zeros (A). @@ -333,7 +304,7 @@ class Bundle { } getJSModulePaths() { - return this._modules.filter(function(module) { + return super.getModules().filter(function(module) { // Filter out non-js files. Like images etc. return !module.virtual; }).map(function(module) { @@ -343,7 +314,7 @@ class Bundle { getDebugInfo() { return [ - '

Main Module:

' + this._mainModuleId + '
', + '

Main Module:

' + super.getMainModuleId() + '
', '', '

Module paths and transformed code:

', - this._modules.map(function(m) { + super.getModules().map(function(m) { return '

Path:

' + m.sourcePath + '

Source:

' + '
'; @@ -369,10 +340,9 @@ class Bundle { } return { - modules: this._modules, - assets: this._assets, + ...super.toJSON(), sourceMapUrl: this._sourceMapUrl, - mainModuleId: this._mainModuleId, + mainModuleId: super.getMainModuleId(), numPrependedModules: this._numPrependedModules, numRequireCalls: this._numRequireCalls, }; @@ -380,18 +350,12 @@ class Bundle { static fromJSON(json) { const bundle = new Bundle(json.sourceMapUrl); - bundle._mainModuleId = json.mainModuleId; - bundle._assets = json.assets; - bundle._modules = json.modules; + bundle._sourceMapUrl = json.sourceMapUrl; bundle._numPrependedModules = json.numPrependedModules; bundle._numRequireCalls = json.numRequireCalls; - Object.freeze(bundle._modules); - Object.seal(bundle._modules); - Object.freeze(bundle._assets); - Object.seal(bundle._assets); - bundle._finalized = true; + BundleBase.fromJSON(bundle, json); return bundle; } diff --git a/react-packager/src/Bundler/BundleBase.js b/react-packager/src/Bundler/BundleBase.js new file mode 100644 index 00000000..635f87e7 --- /dev/null +++ b/react-packager/src/Bundler/BundleBase.js @@ -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; diff --git a/react-packager/src/Bundler/HMRBundle.js b/react-packager/src/Bundler/HMRBundle.js new file mode 100644 index 00000000..fa8c95db --- /dev/null +++ b/react-packager/src/Bundler/HMRBundle.js @@ -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; diff --git a/react-packager/src/Bundler/__tests__/Bundle-test.js b/react-packager/src/Bundler/__tests__/Bundle-test.js index 6dd3d3b7..1180f0a2 100644 --- a/react-packager/src/Bundler/__tests__/Bundle-test.js +++ b/react-packager/src/Bundler/__tests__/Bundle-test.js @@ -10,92 +10,106 @@ 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'); -var ModuleTransport = require('../../lib/ModuleTransport'); -var UglifyJS = require('uglify-js'); - -describe('Bundle', function() { +describe('Bundle', () => { var bundle; - beforeEach(function() { + beforeEach(() => { bundle = new Bundle('test_url'); - bundle.getSourceMap = jest.genMockFn().mockImpl(function() { + bundle.getSourceMap = jest.genMockFn().mockImpl(() => { return 'test-source-map'; }); }); - describe('source bundle', function() { - it('should create a bundle and get the source', function() { - bundle.addModule(new ModuleTransport({ - code: 'transformed foo;', - sourceCode: 'source foo', - sourcePath: 'foo path', - })); - bundle.addModule(new ModuleTransport({ - code: 'transformed bar;', - sourceCode: 'source bar', - sourcePath: 'bar path', - })); - - bundle.finalize({}); - expect(bundle.getSource({dev: true})).toBe([ - 'transformed foo;', - 'transformed bar;', - '\/\/# sourceMappingURL=test_url' - ].join('\n')); - }); - - it('should be ok to leave out the source map url', function() { - var p = new Bundle(); - p.addModule(new ModuleTransport({ - code: 'transformed foo;', - sourceCode: 'source foo', - sourcePath: 'foo path', - })); - p.addModule(new ModuleTransport({ - code: 'transformed bar;', - sourceCode: 'source bar', - sourcePath: 'bar path', - })); - - p.finalize({}); - expect(p.getSource({dev: true})).toBe([ - 'transformed foo;', - 'transformed bar;', - ].join('\n')); - }); - - it('should create a bundle and add run module code', function() { - bundle.addModule(new ModuleTransport({ - code: 'transformed foo;', - sourceCode: 'source foo', - sourcePath: 'foo path' - })); - - bundle.addModule(new ModuleTransport({ - code: 'transformed bar;', - sourceCode: 'source bar', - sourcePath: 'bar path' - })); - - bundle.setMainModuleId('foo'); - bundle.finalize({ - runBeforeMainModule: ['bar'], - runMainModule: true, + describe('source bundle', () => { + pit('should create a bundle and get the source', () => { + return Promise.resolve().then(() => { + return addModule({ + bundle, + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path', + }); + }).then(() => { + return addModule({ + bundle, + code: 'transformed bar;', + sourceCode: 'source bar', + sourcePath: 'bar path', + }); + }).then(() => { + bundle.finalize({}); + expect(bundle.getSource({dev: true})).toBe([ + 'transformed foo;', + 'transformed bar;', + '\/\/# sourceMappingURL=test_url' + ].join('\n')); }); - expect(bundle.getSource({dev: true})).toBe([ - 'transformed foo;', - 'transformed bar;', - ';require("bar");', - ';require("foo");', - '\/\/# sourceMappingURL=test_url', - ].join('\n')); }); - it('should get minified source', function() { - var minified = { + pit('should be ok to leave out the source map url', () => { + const otherBundle = new Bundle(); + return Promise.resolve().then(() => { + return addModule({ + bundle: otherBundle, + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path', + }); + }).then(() => { + return addModule({ + bundle: otherBundle, + code: 'transformed bar;', + sourceCode: 'source bar', + sourcePath: 'bar path', + }); + }).then(() => { + otherBundle.finalize({}); + expect(otherBundle.getSource({dev: true})).toBe([ + 'transformed foo;', + 'transformed bar;', + ].join('\n')); + }); + }); + + pit('should create a bundle and add run module code', () => { + return Promise.resolve().then(() => { + return addModule({ + bundle, + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path', + }); + }).then(() => { + return addModule({ + bundle, + code: 'transformed bar;', + sourceCode: 'source bar', + sourcePath: 'bar path', + }); + }).then(() => { + bundle.setMainModuleId('foo'); + bundle.finalize({ + runBeforeMainModule: ['bar'], + runMainModule: true, + }); + expect(bundle.getSource({dev: true})).toBe([ + 'transformed foo;', + 'transformed bar;', + ';require("bar");', + ';require("foo");', + '\/\/# sourceMappingURL=test_url', + ].join('\n')); + }); + }); + + pit('should get minified source', () => { + const minified = { code: 'minified', map: 'map', }; @@ -104,141 +118,156 @@ describe('Bundle', function() { return minified; }; - bundle.addModule(new ModuleTransport({ - code: 'transformed foo;', - sourceCode: 'source foo', - sourcePath: 'foo path' - })); - bundle.finalize(); - expect(bundle.getMinifiedSourceAndMap({dev: true})).toBe(minified); - }); - }); - - describe('sourcemap bundle', function() { - it('should create sourcemap', function() { - var p = new Bundle('test_url'); - p.addModule(new ModuleTransport({ - code: [ - 'transformed foo', - 'transformed foo', - 'transformed foo', - ].join('\n'), - sourceCode: [ - 'source foo', - 'source foo', - 'source foo', - ].join('\n'), - sourcePath: 'foo path', - })); - p.addModule(new ModuleTransport({ - code: [ - 'transformed bar', - 'transformed bar', - 'transformed bar', - ].join('\n'), - sourceCode: [ - 'source bar', - 'source bar', - 'source bar', - ].join('\n'), - sourcePath: 'bar path', - })); - - p.setMainModuleId('foo'); - p.finalize({ - runBeforeMainModule: [], - runMainModule: true, - }); - var s = p.getSourceMap({dev: true}); - expect(s).toEqual(genSourceMap(p.getModules())); - }); - - it('should combine sourcemaps', function() { - var p = new Bundle('test_url'); - - p.addModule(new ModuleTransport({ - code: 'transformed foo;\n', - map: {name: 'sourcemap foo'}, - sourceCode: 'source foo', - sourcePath: 'foo path' - })); - - p.addModule(new ModuleTransport({ - code: 'transformed foo;\n', - map: {name: 'sourcemap bar'}, - sourceCode: 'source foo', - sourcePath: 'foo path' - })); - - p.addModule(new ModuleTransport({ - code: 'image module;\nimage module;', - virtual: true, - sourceCode: 'image module;\nimage module;', - sourcePath: 'image.png', - })); - - p.setMainModuleId('foo'); - p.finalize({ - runBeforeMainModule: ['InitializeJavaScriptAppEngine'], - runMainModule: true, - }); - - var s = p.getSourceMap({dev: true}); - expect(s).toEqual({ - file: 'bundle.js', - version: 3, - sections: [ - { offset: { line: 0, column: 0 }, map: { name: 'sourcemap foo' } }, - { offset: { line: 2, column: 0 }, map: { name: 'sourcemap bar' } }, - { - offset: { - column: 0, - line: 4 - }, - map: { - file: 'image.png', - mappings: 'AAAA;AACA;', - names: [], - sources: [ 'image.png' ], - sourcesContent: ['image module;\nimage module;'], - version: 3, - } - }, - { - offset: { - column: 0, - line: 6 - }, - map: { - file: 'require-InitializeJavaScriptAppEngine.js', - mappings: 'AAAA;', - names: [], - sources: [ 'require-InitializeJavaScriptAppEngine.js' ], - sourcesContent: [';require("InitializeJavaScriptAppEngine");'], - version: 3, - } - }, - { - offset: { - column: 0, - line: 7 - }, - map: { - file: 'require-foo.js', - mappings: 'AAAA;', - names: [], - sources: [ 'require-foo.js' ], - sourcesContent: [';require("foo");'], - version: 3, - } - }, - ], + return Promise.resolve().then(() => { + return addModule({ + bundle, + code: 'transformed foo;', + sourceCode: 'source foo', + sourcePath: 'foo path', + }); + }).then(() => { + bundle.finalize(); + expect(bundle.getMinifiedSourceAndMap({dev: true})).toBe(minified); }); }); }); - describe('getAssets()', function() { - it('should save and return asset objects', function() { + describe('sourcemap bundle', () => { + pit('should create sourcemap', () => { + const otherBundle = new Bundle('test_url'); + + return Promise.resolve().then(() => { + return addModule({ + bundle: otherBundle, + code: [ + 'transformed foo', + 'transformed foo', + 'transformed foo', + ].join('\n'), + sourceCode: [ + 'source foo', + 'source foo', + 'source foo', + ].join('\n'), + sourcePath: 'foo path', + }); + }).then(() => { + return addModule({ + bundle: otherBundle, + code: [ + 'transformed bar', + 'transformed bar', + 'transformed bar', + ].join('\n'), + sourceCode: [ + 'source bar', + 'source bar', + 'source bar', + ].join('\n'), + sourcePath: 'bar path', + }); + }).then(() => { + otherBundle.setMainModuleId('foo'); + otherBundle.finalize({ + runBeforeMainModule: [], + runMainModule: true, + }); + const sourceMap = otherBundle.getSourceMap({dev: true}); + expect(sourceMap).toEqual(genSourceMap(otherBundle.getModules())); + }); + }); + + pit('should combine sourcemaps', () => { + const otherBundle = new Bundle('test_url'); + + return Promise.resolve().then(() => { + return addModule({ + bundle: otherBundle, + code: 'transformed foo;\n', + sourceCode: 'source foo', + map: {name: 'sourcemap foo'}, + sourcePath: 'foo path', + }); + }).then(() => { + return addModule({ + bundle: otherBundle, + code: 'transformed bar;\n', + sourceCode: 'source bar', + map: {name: 'sourcemap bar'}, + sourcePath: 'bar path', + }); + }).then(() => { + return addModule({ + bundle: otherBundle, + code: 'image module;\nimage module;', + virtual: true, + sourceCode: 'image module;\nimage module;', + sourcePath: 'image.png', + }); + }).then(() => { + otherBundle.setMainModuleId('foo'); + otherBundle.finalize({ + runBeforeMainModule: ['InitializeJavaScriptAppEngine'], + runMainModule: true, + }); + + const sourceMap = otherBundle.getSourceMap({dev: true}); + expect(sourceMap).toEqual({ + file: 'bundle.js', + version: 3, + sections: [ + { offset: { line: 0, column: 0 }, map: { name: 'sourcemap foo' } }, + { offset: { line: 2, column: 0 }, map: { name: 'sourcemap bar' } }, + { + offset: { + column: 0, + line: 4 + }, + map: { + file: 'image.png', + mappings: 'AAAA;AACA;', + names: [], + sources: [ 'image.png' ], + sourcesContent: ['image module;\nimage module;'], + version: 3, + } + }, + { + offset: { + column: 0, + line: 6 + }, + map: { + file: 'require-InitializeJavaScriptAppEngine.js', + mappings: 'AAAA;', + names: [], + sources: [ 'require-InitializeJavaScriptAppEngine.js' ], + sourcesContent: [';require("InitializeJavaScriptAppEngine");'], + version: 3, + } + }, + { + offset: { + column: 0, + line: 7 + }, + map: { + file: 'require-foo.js', + mappings: 'AAAA;', + names: [], + sources: [ 'require-foo.js' ], + sourcesContent: [';require("foo");'], + version: 3, + } + }, + ], + }); + }); + }); + }); + + describe('getAssets()', () => { + it('should save and return asset objects', () => { var p = new Bundle('test_url'); var asset1 = {}; var asset2 = {}; @@ -249,22 +278,27 @@ describe('Bundle', function() { }); }); - describe('getJSModulePaths()', function() { - it('should return module paths', function() { - var p = new Bundle('test_url'); - p.addModule(new ModuleTransport({ - code: 'transformed foo;\n', - sourceCode: 'source foo', - sourcePath: 'foo path' - })); - p.addModule(new ModuleTransport({ - code: 'image module;\nimage module;', - virtual: true, - sourceCode: 'image module;\nimage module;', - sourcePath: 'image.png', - })); - - expect(p.getJSModulePaths()).toEqual(['foo path']); + describe('getJSModulePaths()', () => { + pit('should return module paths', () => { + var otherBundle = new Bundle('test_url'); + return Promise.resolve().then(() => { + return addModule({ + bundle: otherBundle, + code: 'transformed foo;\n', + sourceCode: 'source foo', + sourcePath: 'foo path', + }); + }).then(() => { + return addModule({ + bundle: otherBundle, + code: 'image module;\nimage module;', + virtual: true, + sourceCode: 'image module;\nimage module;', + sourcePath: 'image.png', + }); + }).then(() => { + expect(otherBundle.getJSModulePaths()).toEqual(['foo path']); + }); }); }); }); @@ -302,3 +336,20 @@ function genSourceMap(modules) { } 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} + ); +} diff --git a/react-packager/src/Bundler/__tests__/Bundler-test.js b/react-packager/src/Bundler/__tests__/Bundler-test.js index ccdc8496..467cf90d 100644 --- a/react-packager/src/Bundler/__tests__/Bundler-test.js +++ b/react-packager/src/Bundler/__tests__/Bundler-test.js @@ -161,46 +161,29 @@ describe('Bundler', function() { runBeforeMainModule: [], runModule: true, sourceMapUrl: 'source_map_url', - }).then(function(p) { - expect(p.addModule.mock.calls[0][0]).toEqual({ - name: 'foo', - code: 'lol transformed /root/foo.js lol', - map: 'sourcemap /root/foo.js', - sourceCode: 'source /root/foo.js', - sourcePath: '/root/foo.js', - }); + }).then(bundle => { + const ithAddedModule = (i) => bundle.addModule.mock.calls[i][2].path; - expect(p.addModule.mock.calls[1][0]).toEqual({ - name: 'bar', - code: 'lol transformed /root/bar.js lol', - map: 'sourcemap /root/bar.js', - sourceCode: 'source /root/bar.js', - sourcePath: '/root/bar.js' - }); + expect(ithAddedModule(0)).toEqual('/root/foo.js'); + expect(ithAddedModule(1)).toEqual('/root/bar.js'); + expect(ithAddedModule(2)).toEqual('/root/img/img.png'); + expect(ithAddedModule(3)).toEqual('/root/img/new_image.png'); + expect(ithAddedModule(4)).toEqual('/root/file.json'); - var imgModule_DEPRECATED = { + expect(bundle.finalize.mock.calls[0]).toEqual([ + {runMainModule: true, runBeforeMainModule: []} + ]); + + expect(bundle.addAsset.mock.calls).toContain([{ __packager_asset: true, path: '/root/img/img.png', uri: 'img', width: 25, height: 50, deprecated: true, - }; + }]); - expect(p.addModule.mock.calls[2][0]).toEqual({ - 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 = { + expect(bundle.addAsset.mock.calls).toContain([{ __packager_asset: true, fileSystemLocation: '/root/img', httpServerLocation: '/assets/img', @@ -215,41 +198,7 @@ describe('Bundler', function() { hash: 'i am a hash', name: 'img', 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 //expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length); diff --git a/react-packager/src/Bundler/index.js b/react-packager/src/Bundler/index.js index ebac145c..1cc28f23 100644 --- a/react-packager/src/Bundler/index.js +++ b/react-packager/src/Bundler/index.js @@ -18,6 +18,7 @@ const Cache = require('../DependencyResolver/Cache'); const Transformer = require('../JSTransformer'); const Resolver = require('../Resolver'); const Bundle = require('./Bundle'); +const HMRBundle = require('./HMRBundle'); const PrepackBundle = require('./PrepackBundle'); const Activity = require('../Activity'); const ModuleTransport = require('../lib/ModuleTransport'); @@ -155,31 +156,54 @@ class Bundler { 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, runModule: runMainModule, runBeforeMainModule, - sourceMapUrl, dev: isDev, + includeSystemDependencies, platform, unbundle: isUnbundle, 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'); let transformEventId; - const moduleSystem = this._resolver.getModuleSystemDependencies( - { dev: isDev, platform, isUnbundle } - ); - return this.getDependencies(entryFile, isDev, platform).then((response) => { Activity.endEvent(findEventId); + bundle.setMainModuleId(response.mainModuleId); transformEventId = Activity.startEvent('transform'); - // Prepend the module system polyfill to the top of dependencies - var dependencies = moduleSystem.concat(response.dependencies); + const moduleSystemDeps = includeSystemDependencies + ? 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; if (process.stdout.isTTY) { @@ -191,34 +215,41 @@ class Bundler { }); } - bbundle.setMainModuleId(response.mainModuleId); - bbundle.setNumPrependedModules( - response.numPrependedDependencies + moduleSystem.length); return Promise.all( dependencies.map( - module => this._transformModule( - bbundle, + module => { + return this._transformModule( + bundle, + response, + module, + platform, + hot, + ).then(transformed => { + if (bar) { + bar.tick(); + } + + return { + module, + transformed, + }; + }); + } + ) + ).then(transformedModules => Promise.all( + transformedModules.map(({module, transformed}) => { + return bundle.addModule( + this._resolver, response, module, - platform, - hot, - ).then(transformed => { - if (bar) { - bar.tick(); - } - return this._wrapTransformedModule(response, module, transformed); - }) - ) - ); - }).then((transformedModules) => { + transformed, + ); + }) + )); + }).then(() => { Activity.endEvent(transformEventId); - - transformedModules.forEach(function(moduleTransport) { - bbundle.addModule(moduleTransport); - }); - - bbundle.finalize({runBeforeMainModule, runMainModule}); - return bbundle; + 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) { if (module.isAsset()) { 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() { return this._resolver.getDebugInfo(); }