diff --git a/packages/metro-bundler/src/Server/__tests__/Server-test.js b/packages/metro-bundler/src/Server/__tests__/Server-test.js index 10eb8395..33050bd9 100644 --- a/packages/metro-bundler/src/Server/__tests__/Server-test.js +++ b/packages/metro-bundler/src/Server/__tests__/Server-test.js @@ -27,8 +27,16 @@ jest .mock('../../lib/GlobalTransformCache') .mock('../../DeltaBundler/Serializers'); +const DeltaBundler = require('../../DeltaBundler'); + describe('processRequest', () => { - let Bundler, Server, AssetServer, symbolicate, Serializers; + let Bundler; + let Server; + let AssetServer; + let symbolicate; + let Serializers; + const lastModified = new Date(); + beforeEach(() => { jest.useFakeTimers(); jest.resetModules(); @@ -53,7 +61,7 @@ describe('processRequest', () => { const makeRequest = (reqHandler, requrl, reqOptions) => new Promise(resolve => reqHandler( - {url: requrl, headers: {}, ...reqOptions}, + {url: requrl, headers: {host: 'localhost:8081'}, ...reqOptions}, { statusCode: 200, headers: {}, @@ -79,6 +87,18 @@ describe('processRequest', () => { let requestHandler; beforeEach(() => { + Serializers.fullBundle.mockReturnValue( + Promise.resolve({ + bundle: 'this is the source', + numModifiedFiles: 38, + lastModified, + }), + ); + + Serializers.fullSourceMap.mockReturnValue( + Promise.resolve('this is the source map'), + ); + Bundler.prototype.bundle = jest.fn(() => Promise.resolve({ getModules: () => [], @@ -118,12 +138,12 @@ describe('processRequest', () => { ).then(response => expect(response.body).toEqual('this is the source')); }); - it('returns ETag header on request of *.bundle', () => { + it('returns Last-Modified header on request of *.bundle', () => { return makeRequest( requestHandler, 'mybundle.bundle?runModule=true', ).then(response => { - expect(response.getHeader('ETag')).toBeDefined(); + expect(response.getHeader('Last-Modified')).toBeDefined(); }); }); @@ -132,7 +152,7 @@ describe('processRequest', () => { requestHandler, 'mybundle.bundle?runModule=true', ).then(response => { - expect(response.getHeader('X-Metro-Files-Changed-Count')).toBeDefined(); + expect(response.getHeader('X-Metro-Files-Changed-Count')).toEqual('38'); }); }); @@ -141,15 +161,15 @@ describe('processRequest', () => { requestHandler, 'mybundle.bundle?runModule=true', ).then(response => { - expect(response.getHeader('Content-Length')).toBe( - Buffer.byteLength(response.body), + expect(response.getHeader('Content-Length')).toEqual( + '' + Buffer.byteLength(response.body), ); }); }); - it('returns 304 on request of *.bundle when if-none-match equals the ETag', () => { + it('returns 304 on request of *.bundle when if-modified-since equals Last-Modified', () => { return makeRequest(requestHandler, 'mybundle.bundle?runModule=true', { - headers: {'if-none-match': 'this is an etag'}, + headers: {'if-modified-since': lastModified.toUTCString()}, }).then(response => { expect(response.statusCode).toEqual(304); }); @@ -168,8 +188,9 @@ describe('processRequest', () => { 'index.ios.includeRequire.bundle', ).then(response => { expect(response.body).toEqual('this is the source'); - expect(Bundler.prototype.bundle).toBeCalledWith({ + expect(Serializers.fullBundle).toBeCalledWith(expect.any(DeltaBundler), { assetPlugins: [], + deltaBundleId: expect.any(String), dev: true, entryFile: 'index.ios.js', entryModuleOnly: false, @@ -184,7 +205,7 @@ describe('processRequest', () => { resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], runModule: true, - sourceMapUrl: 'index.ios.includeRequire.map', + sourceMapUrl: 'http://localhost:8081/index.ios.includeRequire.map', unbundle: false, }); }); @@ -196,8 +217,9 @@ describe('processRequest', () => { 'index.bundle?platform=ios', ).then(function(response) { expect(response.body).toEqual('this is the source'); - expect(Bundler.prototype.bundle).toBeCalledWith({ + expect(Serializers.fullBundle).toBeCalledWith(expect.any(DeltaBundler), { assetPlugins: [], + deltaBundleId: expect.any(String), dev: true, entryFile: 'index.js', entryModuleOnly: false, @@ -212,7 +234,7 @@ describe('processRequest', () => { resolutionResponse: null, runBeforeMainModule: ['InitializeCore'], runModule: true, - sourceMapUrl: 'index.map?platform=ios', + sourceMapUrl: 'http://localhost:8081/index.map?platform=ios', unbundle: false, }); }); @@ -224,8 +246,9 @@ describe('processRequest', () => { 'index.bundle?assetPlugin=assetPlugin1&assetPlugin=assetPlugin2', ).then(function(response) { expect(response.body).toEqual('this is the source'); - expect(Bundler.prototype.bundle).toBeCalledWith({ + expect(Serializers.fullBundle).toBeCalledWith(expect.any(DeltaBundler), { assetPlugins: ['assetPlugin1', 'assetPlugin2'], + deltaBundleId: expect.any(String), dev: true, entryFile: 'index.js', entryModuleOnly: false, @@ -241,130 +264,13 @@ describe('processRequest', () => { runBeforeMainModule: ['InitializeCore'], runModule: true, sourceMapUrl: - 'index.map?assetPlugin=assetPlugin1&assetPlugin=assetPlugin2', + 'http://localhost:8081/index.map?assetPlugin=assetPlugin1&assetPlugin=assetPlugin2', unbundle: false, }); }); }); - describe('file changes', () => { - it('does not rebuild the bundles that contain a file when that file is changed', () => { - const bundleFunc = jest.fn(); - bundleFunc - .mockReturnValueOnce( - Promise.resolve({ - getModules: () => [], - getSource: () => 'this is the first source', - getSourceMap: () => {}, - getSourceMapString: () => 'this is the source map', - getEtag: () => () => 'this is an etag', - }), - ) - .mockReturnValue( - Promise.resolve({ - getModules: () => [], - getSource: () => 'this is the rebuilt source', - getSourceMap: () => {}, - getSourceMapString: () => 'this is the source map', - getEtag: () => () => 'this is an etag', - }), - ); - - Bundler.prototype.bundle = bundleFunc; - - server = new Server(options); - - requestHandler = server.processRequest.bind(server); - - makeRequest( - requestHandler, - 'mybundle.bundle?runModule=true', - ).done(response => { - expect(response.body).toEqual('this is the first source'); - expect(bundleFunc.mock.calls.length).toBe(1); - }); - - jest.runAllTicks(); - - server.onFileChange('all', options.projectRoots[0] + 'path/file.js'); - jest.runAllTimers(); - jest.runAllTicks(); - - expect(bundleFunc.mock.calls.length).toBe(1); - - makeRequest( - requestHandler, - 'mybundle.bundle?runModule=true', - ).done(response => - expect(response.body).toEqual('this is the rebuilt source'), - ); - jest.runAllTicks(); - }); - - it( - 'does not rebuild the bundles that contain a file ' + - 'when that file is changed, even when hot loading is enabled', - () => { - const bundleFunc = jest.fn(); - bundleFunc - .mockReturnValueOnce( - Promise.resolve({ - getModules: () => [], - getSource: () => 'this is the first source', - getSourceMap: () => {}, - getSourceMapString: () => 'this is the source map', - getEtag: () => () => 'this is an etag', - }), - ) - .mockReturnValue( - Promise.resolve({ - getModules: () => [], - getSource: () => 'this is the rebuilt source', - getSourceMap: () => {}, - getSourceMapString: () => 'this is the source map', - getEtag: () => () => 'this is an etag', - }), - ); - - Bundler.prototype.bundle = bundleFunc; - - server = new Server(options); - server.setHMRFileChangeListener(() => {}); - - requestHandler = server.processRequest.bind(server); - - makeRequest( - requestHandler, - 'mybundle.bundle?runModule=true', - ).done(response => { - expect(response.body).toEqual('this is the first source'); - expect(bundleFunc.mock.calls.length).toBe(1); - }); - - jest.runAllTicks(); - - server.onFileChange('all', options.projectRoots[0] + 'path/file.js'); - jest.runAllTimers(); - jest.runAllTicks(); - - expect(bundleFunc.mock.calls.length).toBe(1); - server.setHMRFileChangeListener(null); - - makeRequest( - requestHandler, - 'mybundle.bundle?runModule=true', - ).done(response => { - expect(response.body).toEqual('this is the rebuilt source'); - expect(bundleFunc.mock.calls.length).toBe(2); - }); - jest.runAllTicks(); - }, - ); - }); - describe('Generate delta bundle endpoint', () => { - Serializers; - it('should generate a new delta correctly', () => { Serializers.deltaBundle.mockImplementation(async (_, options) => { expect(options.deltaBundleId).toBe(undefined); diff --git a/packages/metro-bundler/src/Server/index.js b/packages/metro-bundler/src/Server/index.js index 1ce07047..7d4437a3 100644 --- a/packages/metro-bundler/src/Server/index.js +++ b/packages/metro-bundler/src/Server/index.js @@ -97,7 +97,7 @@ export type Options = {| +sourceExts: ?Array, +transformCache: TransformCache, transformModulePath?: string, - useDeltaBundler: boolean, + useDeltaBundler?: boolean, // TODO: remove this, since it's no longer used watch?: boolean, workerPath: ?string, |}; @@ -143,6 +143,8 @@ const FILES_CHANGED_COUNT_REBUILD = -1; const bundleDeps = new WeakMap(); const NODE_MODULES = `${path.sep}node_modules${path.sep}`; +const USE_DELTA_BUNDLER = true; + class Server { _opts: { assetExts: Array, @@ -226,7 +228,6 @@ class Server { transformCache: options.transformCache, transformModulePath: options.transformModulePath || defaults.transformModulePath, - useDeltaBundler: options.useDeltaBundler, watch: options.watch || false, workerPath: options.workerPath, }; @@ -448,7 +449,7 @@ class Server { +minify: boolean, +generateSourceMaps: boolean, }): Promise> { - if (this._opts.useDeltaBundler) { + if (USE_DELTA_BUNDLER) { const bundleOptions = { ...Server.DEFAULT_BUNDLE_OPTIONS, ...options, @@ -855,7 +856,7 @@ class Server { return; } - if (this._opts.useDeltaBundler) { + if (USE_DELTA_BUNDLER) { if (requestType === 'bundle') { await this._processBundleUsingDeltaBundler(req, res); return; @@ -1214,7 +1215,7 @@ class Server { async _sourceMapForURL(reqUrl: string): Promise { const options: DeltaBundlerOptions = this._getOptionsFromUrl(reqUrl); - if (this._opts.useDeltaBundler) { + if (USE_DELTA_BUNDLER) { return await Serializers.fullSourceMapObject(this._deltaBundler, { ...options, deltaBundleId: this.optionsHash(options), diff --git a/packages/metro-bundler/src/index.js b/packages/metro-bundler/src/index.js index 1b8d62fc..d0f7e737 100644 --- a/packages/metro-bundler/src/index.js +++ b/packages/metro-bundler/src/index.js @@ -198,7 +198,6 @@ function toServerOptions(options: Options): ServerOptions { sourceExts: options.sourceExts, transformCache: options.transformCache || TransformCaching.useTempDir(), transformModulePath: options.transformModulePath, - useDeltaBundler: options.useDeltaBundler, watch: typeof options.watch === 'boolean' ? options.watch