diff --git a/packages/metro/src/HmrServer.js b/packages/metro/src/HmrServer.js index 3e8ac80948..c02b9e9df7 100644 --- a/packages/metro/src/HmrServer.js +++ b/packages/metro/src/HmrServer.js @@ -122,6 +122,8 @@ class HmrServer { revisionId: id, }; + await this._handleFileChange(client); + const unlisten = this._bundler .getDeltaBundler() .listen( diff --git a/packages/metro/src/HmrServer/__tests__/HmrServer-test.js b/packages/metro/src/HmrServer/__tests__/HmrServer-test.js index db758cd289..dc63062310 100644 --- a/packages/metro/src/HmrServer/__tests__/HmrServer-test.js +++ b/packages/metro/src/HmrServer/__tests__/HmrServer-test.js @@ -23,6 +23,22 @@ describe('HmrServer', () => { let callbacks; let mockedGraph; + const hiModule = { + dependencies: new Map(), + inverseDependencies: new Set(), + path: '/root/hi', + getSource: () => "alert('hi');", + output: [ + { + type: 'js/module', + data: { + map: [], + code: '__d(function() { alert("hi"); });', + }, + }, + ], + }; + beforeEach(() => { mockedGraph = { dependencies: new Map(), @@ -48,6 +64,16 @@ describe('HmrServer', () => { }, getRevision: getRevisionMock, getRevisionByGraphId: getRevisionByGraphIdMock, + updateGraph: jest.fn().mockResolvedValue({ + revision: { + id: 'rev0', + graph: mockedGraph, + }, + delta: { + modified: new Map(), + deleted: new Set(), + }, + }), }; createModuleIdMock = path => { return path + '-id'; @@ -140,43 +166,71 @@ describe('HmrServer', () => { expect(client).toBe(null); }); - it('should return the correctly formatted HMR message after a file change', async () => { + it('should send an initial update when a client connects', async () => { const sendMessage = jest.fn(); + incrementalBundlerMock.updateGraph.mockResolvedValue({ + revision: { + id: 'rev0', + graph: mockedGraph, + }, + delta: { + modified: new Map([[hiModule.path, hiModule]]), + deleted: new Set(['/root/bye']), + }, + }); + await hmrServer.onClientConnect( '/hot?bundleEntry=EntryPoint.js&platform=ios', sendMessage, ); - const hiModule = { - dependencies: new Map(), - inverseDependencies: new Set(), - path: '/root/hi', - getSource: () => "alert('hi');", - output: [ - { - type: 'js/module', - data: { - map: [], - code: '__d(function() { alert("hi"); });', - }, + const messages = sendMessage.mock.calls.map(call => JSON.parse(call[0])); + expect(messages).toMatchObject([ + { + type: 'update-start', + }, + { + type: 'update', + body: { + revisionId: 'rev0', + modules: [ + [ + '/root/hi-id', + '__d(function() { alert("hi"); },"/root/hi-id",[],"hi",{});', + ], + ], + deleted: ['/root/bye-id'], + sourceURLs: ['/root/hi'], + sourceMappingURLs: [expect.anything()], }, - ], - }; + }, + { + type: 'update-done', + }, + ]); + }); + it('should return the correctly formatted HMR message after a file change', async () => { + const sendMessage = jest.fn(); - incrementalBundlerMock.updateGraph = jest.fn().mockReturnValue( - Promise.resolve({ - revision: { - id: 'revision-id', - graph: mockedGraph, - }, - delta: { - modified: new Map([['/root/hi', hiModule]]), - deleted: new Set(['/root/bye']), - }, - }), + await hmrServer.onClientConnect( + '/hot?bundleEntry=EntryPoint.js&platform=ios', + sendMessage, ); + sendMessage.mockReset(); + + incrementalBundlerMock.updateGraph.mockResolvedValue({ + revision: { + id: 'rev1', + graph: mockedGraph, + }, + delta: { + modified: new Map([[hiModule.path, hiModule]]), + deleted: new Set(['/root/bye']), + }, + }); + const promise = callbacks.get(mockedGraph)(); jest.runAllTimers(); await promise; @@ -190,7 +244,7 @@ describe('HmrServer', () => { { type: 'update', body: { - revisionId: 'revision-id', + revisionId: 'rev1', modules: [ [ '/root/hi-id', @@ -233,7 +287,9 @@ describe('HmrServer', () => { sendMessage, ); - incrementalBundlerMock.updateGraph = jest.fn().mockImplementation(() => { + sendMessage.mockReset(); + + incrementalBundlerMock.updateGraph.mockImplementation(() => { const transformError = new SyntaxError('test syntax error'); transformError.type = 'TransformError'; transformError.filename = 'EntryPoint.js'; @@ -243,24 +299,29 @@ describe('HmrServer', () => { await callbacks.get(mockedGraph)(); - expect(JSON.parse(sendMessage.mock.calls[0][0])).toEqual({ - type: 'update-start', - }); - const sentErrorMessage = JSON.parse(sendMessage.mock.calls[1][0]); - expect(sentErrorMessage).toMatchObject({type: 'error'}); - expect(sentErrorMessage.body).toMatchObject({ - type: 'TransformError', - message: 'test syntax error', - errors: [ - { - description: 'test syntax error', - filename: 'EntryPoint.js', - lineNumber: 123, + const messages = sendMessage.mock.calls.map(call => JSON.parse(call[0])); + + expect(messages).toMatchObject([ + { + type: 'update-start', + }, + { + type: 'error', + body: { + type: 'TransformError', + message: 'test syntax error', + errors: [ + { + description: 'test syntax error', + filename: 'EntryPoint.js', + lineNumber: 123, + }, + ], }, - ], - }); - expect(JSON.parse(sendMessage.mock.calls[2][0])).toEqual({ - type: 'update-done', - }); + }, + { + type: 'update-done', + }, + ]); }); }); diff --git a/packages/metro/src/lib/bundle-modules/DeltaClient/__tests__/createDeltaClient-test.js b/packages/metro/src/lib/bundle-modules/DeltaClient/__tests__/createDeltaClient-test.js index d111612d03..e046be1717 100644 --- a/packages/metro/src/lib/bundle-modules/DeltaClient/__tests__/createDeltaClient-test.js +++ b/packages/metro/src/lib/bundle-modules/DeltaClient/__tests__/createDeltaClient-test.js @@ -43,12 +43,13 @@ function createDelta(revisionId, modules = [], deleted = []) { describe('createDeltaClient', () => { let fetch; beforeEach(() => { - global.__DEV__ = true; + global.__DEV__ = false; fetch = global.fetch = jest.fn(); global.URL = URL; global.Response = Response; global.Request = Request; global.Headers = Headers; + console.error = jest.fn(); }); it('retrieves a bundle from cache and patches it with a delta bundle', async () => { @@ -190,111 +191,197 @@ post" }); }); - describe('HMR', () => { + describe('Updates', () => { + const flushPromises = async () => + await new Promise(resolve => setImmediate(resolve)); + + const emit = (name, ...args) => { + WebSocketHMRClient.prototype.on.mock.calls + .filter(call => call[0] === name) + .map(call => call[1](...args)); + }; + beforeEach(() => { + global.__DEV__ = true; + global.clients = { + get: jest.fn().mockResolvedValue({ + postMessage: jest.fn(), + }), + }; const bundle = createBundle('rev0', [0]); - const delta = createDelta('rev1', [1], [0]); getBundle.mockResolvedValue(bundle); - fetch.mockResolvedValue(new Response(JSON.stringify(delta))); + WebSocketHMRClient.prototype.on.mockClear(); WebSocketHMRClient.mockClear(); }); it('sets up the HMR client', async () => { - const deltaClient = createDeltaClient({hot: true}); + const deltaClient = createDeltaClient(); - await deltaClient({ + deltaClient({ clientId: 'client0', request: new Request('http://localhost/bundles/cool.bundle'), }); + await flushPromises(); + expect(WebSocketHMRClient).toHaveBeenCalledWith( - 'ws://localhost/hot?revisionId=rev1', + 'ws://localhost/hot?revisionId=rev0', ); }); it('sets up the HMR client (HTTPS)', async () => { - const deltaClient = createDeltaClient({hot: true}); + const deltaClient = createDeltaClient(); - await deltaClient({ + deltaClient({ clientId: 'client0', request: new Request('https://localhost/bundles/cool.bundle'), }); + await flushPromises(); + expect(WebSocketHMRClient).toHaveBeenCalledWith( - 'wss://localhost/hot?revisionId=rev1', + 'wss://localhost/hot?revisionId=rev0', ); }); it('accepts a custom getHmrServerUrl function', async () => { const getHmrServerUrl = jest.fn().mockReturnValue('ws://whatever'); - const deltaClient = createDeltaClient({hot: true, getHmrServerUrl}); + const deltaClient = createDeltaClient({getHmrServerUrl}); const bundleReq = new Request('https://localhost/bundles/cool.bundle'); - await deltaClient({ + + deltaClient({ clientId: 'client0', request: bundleReq, }); - expect(getHmrServerUrl).toHaveBeenCalledWith(bundleReq, 'rev1'); + await flushPromises(); + + expect(getHmrServerUrl).toHaveBeenCalledWith(bundleReq, 'rev0'); expect(WebSocketHMRClient).toHaveBeenCalledWith('ws://whatever'); }); - it('sends an HMR update to clients', async () => { + it('retrieves a bundle from cache and patches it with the initial update', async () => { + const deltaClient = createDeltaClient(); + + const bundleReq = new Request('https://localhost/bundles/cool.bundle'); + const promise = deltaClient({ + clientId: 'client0', + request: bundleReq, + }); + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev1', + modules: [[1, '0.1']], + deleted: [0], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + const response = await promise; + + expect(setBundle).toHaveBeenCalledWith(bundleReq, { + base: true, + revisionId: 'rev1', + pre: 'pre("rev0");', + post: 'post("rev0");', + modules: [[1, '0.1']], + }); + expect(await response.text()).toMatchInlineSnapshot(` +"pre(\\"rev0\\"); +0.1 +post(\\"rev0\\");" +`); + }); + + it('sends an update message to clients', async () => { const clientMock = { postMessage: jest.fn(), }; global.clients = { get: jest.fn().mockResolvedValue(clientMock), }; - const deltaClient = createDeltaClient({hot: true}); + const deltaClient = createDeltaClient(); - await deltaClient({ + deltaClient({ clientId: 'client0', request: new Request('https://localhost/bundles/cool.bundle'), }); - const hmrUpdate = { - revisionId: 'rev2', - modules: [[0, '0.1']], + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], deleted: [], sourceMappingURLs: [], sourceURLs: [], + }); + emit('update-done'); + + const update = { + revisionId: 'rev1', + modules: [[1, '0.1']], + deleted: [0], + sourceMappingURLs: [], + sourceURLs: [], }; - const updateHandlers = WebSocketHMRClient.mock.instances[0].on.mock.calls.filter( - call => call[0] === 'update', - ); - updateHandlers.forEach(updateHandler => updateHandler[1](hmrUpdate)); + emit('update-start'); + emit('update', update); + emit('update-done'); + expect(global.clients.get).toHaveBeenCalledWith('client0'); - // The default update function is asynchronous. - await new Promise(resolve => setImmediate(resolve)); + await flushPromises(); + expect(clientMock.postMessage).toHaveBeenCalledWith({ - type: 'HMR_UPDATE', - body: hmrUpdate, + type: 'METRO_UPDATE', + update, }); }); - it('patches the cached bundle on update', async () => { - const deltaClient = createDeltaClient({hot: true}); + it('patches the cached bundle on later update', async () => { + const deltaClient = createDeltaClient(); const bundleReq = new Request('https://localhost/bundles/cool.bundle'); - await deltaClient({ + + deltaClient({ clientId: 'client0', request: bundleReq, }); - const hmrUpdate = { - revisionId: 'rev2', + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], + deleted: [], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + const update = { + revisionId: 'rev1', modules: [[1, '0.1']], deleted: [0], sourceMappingURLs: [], sourceURLs: [], }; - const updateHandlers = WebSocketHMRClient.mock.instances[0].on.mock.calls.filter( - call => call[0] === 'update', - ); - updateHandlers.forEach(updateHandler => updateHandler[1](hmrUpdate)); + emit('update-start'); + emit('update', update); + emit('update-done'); + expect(setBundle).toHaveBeenCalledWith(bundleReq, { base: true, - revisionId: 'rev2', + revisionId: 'rev1', pre: 'pre("rev0");', post: 'post("rev0");', modules: [[1, '0.1']], @@ -303,89 +390,139 @@ post" it('accepts a custom onUpdate function', async () => { const onUpdate = jest.fn(); - const deltaClient = createDeltaClient({hot: true, onUpdate}); + const deltaClient = createDeltaClient({onUpdate}); - await deltaClient({ + deltaClient({ clientId: 'client0', request: new Request('https://localhost/bundles/cool.bundle'), }); - const hmrUpdate = { - revisionId: 'rev2', - modules: [[0, '0.1']], + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], deleted: [], sourceMappingURLs: [], sourceURLs: [], + }); + emit('update-done'); + + const update = { + revisionId: 'rev1', + modules: [[1, '0.1']], + deleted: [0], + sourceMappingURLs: [], + sourceURLs: [], }; - const updateHandlers = WebSocketHMRClient.mock.instances[0].on.mock.calls.filter( - call => call[0] === 'update', - ); - updateHandlers.forEach(updateHandler => updateHandler[1](hmrUpdate)); + emit('update-start'); + emit('update', update); + emit('update-done'); - expect(onUpdate).toHaveBeenCalledWith('client0', hmrUpdate); + expect(onUpdate).toHaveBeenCalledWith('client0', update); }); it('only connects once for a given revisionId', async () => { - const bundle = createBundle('rev0', [0]); - const delta = createDelta('rev0', [], []); - getBundle.mockResolvedValue(bundle); - fetch.mockResolvedValue(new Response(JSON.stringify(delta))); const onUpdate = jest.fn(); - const deltaClient = createDeltaClient({hot: true, onUpdate}); + const deltaClient = createDeltaClient({onUpdate}); - await deltaClient({ + deltaClient({ clientId: 'client0', request: new Request('https://localhost/bundles/cool.bundle'), }); - fetch.mockResolvedValue(new Response(JSON.stringify(delta))); - await deltaClient({ + + deltaClient({ clientId: 'client1', request: new Request('https://localhost/bundles/cool.bundle'), }); - const hmrUpdate = { - revisionId: 'rev2', + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], + deleted: [], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + const update = { + revisionId: 'rev1', modules: [[0, '0.1']], deleted: [], sourceMappingURLs: [], sourceURLs: [], }; - expect(WebSocketHMRClient).toHaveBeenCalledTimes(1); - const updateHandlers = WebSocketHMRClient.mock.instances[0].on.mock.calls.filter( - call => call[0] === 'update', - ); - updateHandlers.forEach(updateHandler => updateHandler[1](hmrUpdate)); + emit('update-start'); + emit('update', update); + emit('update-done'); - expect(onUpdate).toHaveBeenCalledWith('client0', hmrUpdate); - expect(onUpdate).toHaveBeenCalledWith('client1', hmrUpdate); + expect(WebSocketHMRClient).toHaveBeenCalledTimes(1); + expect(onUpdate).toHaveBeenNthCalledWith(1, 'client0', update); + expect(onUpdate).toHaveBeenNthCalledWith(2, 'client1', update); }); it('reconnects when a new request comes in', async () => { - const bundle = createBundle('rev0', [0]); - const delta = createDelta('rev0', [], []); - getBundle.mockResolvedValue(bundle); - fetch.mockResolvedValue(new Response(JSON.stringify(delta))); - const onUpdate = jest.fn(); - const deltaClient = createDeltaClient({hot: true, onUpdate}); + const deltaClient = createDeltaClient(); - await deltaClient({ + deltaClient({ clientId: 'client0', request: new Request('https://localhost/bundles/cool.bundle'), }); + await flushPromises(); + expect(WebSocketHMRClient).toHaveBeenCalledTimes(1); - const closeHandlers = WebSocketHMRClient.mock.instances[0].on.mock.calls.filter( - call => call[0] === 'close', - ); - closeHandlers.forEach(handler => handler[1]()); - fetch.mockResolvedValue(new Response(JSON.stringify(delta))); - await deltaClient({ + emit('close'); + + deltaClient({ clientId: 'client1', request: new Request('https://localhost/bundles/cool.bundle'), }); + await flushPromises(); + + emit('close'); + expect(WebSocketHMRClient).toHaveBeenCalledTimes(2); }); + + it('fetches the original bundle if there is a connection error', async () => { + fetch.mockResolvedValue( + new Response(`pre +0 +post +//# offsetTable={"pre":3,"post":4,"modules":[[0,1]],"revisionId":"rev0"}`), + ); + const deltaClient = createDeltaClient(); + + const bundleReq = new Request('https://localhost/bundles/cool.bundle'); + const promise = deltaClient({ + clientId: 'client0', + request: bundleReq, + }); + + await flushPromises(); + + emit('connection-error'); + + const res = await promise; + + expect(fetch).toHaveBeenCalledWith(bundleReq, { + includeCredentials: true, + }); + expect(await res.text()).toMatchInlineSnapshot(` +"pre +0 +post" +`); + }); }); }); diff --git a/packages/metro/src/lib/bundle-modules/DeltaClient/bundleCache.js b/packages/metro/src/lib/bundle-modules/DeltaClient/bundleCache.js index b40b175aab..c31449e776 100644 --- a/packages/metro/src/lib/bundle-modules/DeltaClient/bundleCache.js +++ b/packages/metro/src/lib/bundle-modules/DeltaClient/bundleCache.js @@ -18,7 +18,7 @@ import type {Bundle} from '../types.flow'; const BUNDLE_CACHE_NAME = '__metroBundleCache'; -async function getBundleFromBrowserCache(bundleReq: Request) { +async function getBundleFromBrowserCache(bundleReq: Request): Promise { const res = await fetch(bundleReq, { // This forces using the browser cache, in which the initial bundle request // will have been stored. @@ -48,19 +48,13 @@ async function getBundleFromCustomCache( */ async function getBundle(bundleReq: Request): Promise { const cache = await caches.open(BUNDLE_CACHE_NAME); - let deltaBundle = await getBundleFromCustomCache(cache, bundleReq); + const deltaBundle = await getBundleFromCustomCache(cache, bundleReq); if (deltaBundle != null) { return deltaBundle; } - deltaBundle = await getBundleFromBrowserCache(bundleReq); - - if (deltaBundle != null) { - return deltaBundle; - } - - return null; + return await getBundleFromBrowserCache(bundleReq); } /** diff --git a/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js b/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js index a8baefc1af..e0724c903c 100644 --- a/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js +++ b/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js @@ -34,7 +34,6 @@ export type GetHmrServerUrl = ( ) => string; export type DeltaClientOptions = {| - +hot?: boolean, +getDeltaBundle?: GetDeltaBundle, +getHmrServerUrl?: GetHmrServerUrl, +onUpdate?: (clientId: string, update: HmrUpdate) => void, @@ -49,30 +48,6 @@ async function fetchBundle(bundleReq: Request): Promise { return stringToBundle(await bundleRes.text()); } -async function getOrFetchBundle( - bundleReq: Request, - getDeltaBundle: GetDeltaBundle, -): Promise { - let bundle = await bundleCache.getBundle(bundleReq); - - if (bundle == null) { - // We couldn't retrieve a delta bundle from either the delta cache nor the - // browser cache. This can happen when the browser cache is cleared but the - // service worker survives. In this case, we retrieve the original bundle. - bundle = await fetchBundle(bundleReq); - } else { - try { - const delta = await getDeltaBundle(bundleReq, bundle.revisionId); - bundle = patchBundle(bundle, delta); - } catch (error) { - console.error('[SW] Error retrieving delta bundle', error); - bundle = await fetchBundle(bundleReq); - } - } - - return bundle; -} - function defaultGetHmrServerUrl( bundleReq: Request, revisionId: string, @@ -100,67 +75,112 @@ function defaultOnUpdate(clientId: string, update: HmrUpdate) { clients.get(clientId).then(client => { if (client != null) { client.postMessage({ - type: 'HMR_UPDATE', - body: update, + type: 'METRO_UPDATE', + update, }); } }); } function createDeltaClient({ - hot = false, getHmrServerUrl = defaultGetHmrServerUrl, getDeltaBundle = defaultGetDeltaBundle, onUpdate = defaultOnUpdate, }: DeltaClientOptions = {}): DeltaClient { - const updateHandlersMap = new Map(); + const clientsByRevId: Map> = new Map(); return async (event: FetchEvent) => { const clientId = event.clientId; - const bundleReq = event.request; + const bundleReq: Request = event.request; - let bundle = await getOrFetchBundle(bundleReq, getDeltaBundle); - - bundleCache.setBundle(bundleReq, bundle); + let bundle = await bundleCache.getBundle(bundleReq); - if (__DEV__ && hot) { - const existingUpdateHandlers = updateHandlersMap.get(bundle.revisionId); - if (existingUpdateHandlers != null) { - existingUpdateHandlers.add(onUpdate.bind(null, clientId)); + if (bundle == null) { + // We couldn't retrieve a delta bundle from either the delta cache nor the + // browser cache. This can happen when the browser cache is cleared but the + // service worker survives. In this case, we retrieve the original bundle. + bundle = await fetchBundle(bundleReq); + } else if (!__DEV__) { + try { + const delta = await getDeltaBundle(bundleReq, bundle.revisionId); + bundle = patchBundle(bundle, delta); + } catch (error) { + console.error('[SW] Error retrieving delta bundle', error); + bundle = await fetchBundle(bundleReq); + } + } else { + const clientIds = clientsByRevId.get(bundle.revisionId); + if (clientIds != null) { + // There's already an update client running for this particular + // revision id. + clientIds.add(clientId); } else { - const updateHandlers = new Set([onUpdate.bind(null, clientId)]); - updateHandlersMap.set(bundle.revisionId, updateHandlers); - - const hmrClient = new WebSocketHMRClient( - getHmrServerUrl(bundleReq, bundle.revisionId), - ); - - hmrClient.on('update', update => { - updateHandlersMap.delete(bundle.revisionId); - updateHandlersMap.set(update.revisionId, updateHandlers); - - for (const updateHandler of updateHandlers) { - updateHandler(update); - } - - bundle = patchBundle(bundle, { - base: false, - revisionId: update.revisionId, - modules: update.modules, - deleted: update.deleted, + const clientIds = new Set([clientId]); + clientsByRevId.set(bundle.revisionId, clientIds); + + try { + let currentBundle = bundle; + + bundle = await new Promise((resolve, reject) => { + let resolved = false; + const wsClient = new WebSocketHMRClient( + getHmrServerUrl(bundleReq, currentBundle.revisionId), + ); + + wsClient.on('connection-error', error => { + reject(error); + }); + + wsClient.on('close', () => { + clientsByRevId.delete(currentBundle.revisionId); + }); + + wsClient.on('error', error => { + if (!resolved) { + reject(error); + return; + } + }); + + wsClient.on('update', update => { + if (resolved) { + // Only notify clients for later updates. + clientIds.forEach(clientId => onUpdate(clientId, update)); + } + + // Transfers all clients to the new revision id. + clientsByRevId.delete(currentBundle.revisionId); + clientsByRevId.set(update.revisionId, clientIds); + + currentBundle = patchBundle(currentBundle, { + base: false, + revisionId: update.revisionId, + modules: update.modules, + deleted: update.deleted, + }); + + bundleCache.setBundle(bundleReq, currentBundle); + + if (!resolved) { + resolved = true; + resolve(currentBundle); + } + }); + + wsClient.enable(); }); - - bundleCache.setBundle(bundleReq, bundle); - }); - - hmrClient.on('close', () => { - updateHandlersMap.delete(bundle.revisionId); - }); - - hmrClient.enable(); + } catch (error) { + console.error( + '[SW] Error connecting to the update server. Try refreshing the page.', + error, + ); + bundle = await fetchBundle(bundleReq); + } } } + bundleCache.setBundle(bundleReq, bundle); + const bundleString = bundleToString(bundle); const bundleStringRes = new Response(bundleString, { status: 200, diff --git a/packages/metro/src/lib/bundle-modules/deltaClientServiceWorker.js b/packages/metro/src/lib/bundle-modules/deltaClientServiceWorker.js index 7b075dc0a5..b957942a37 100644 --- a/packages/metro/src/lib/bundle-modules/deltaClientServiceWorker.js +++ b/packages/metro/src/lib/bundle-modules/deltaClientServiceWorker.js @@ -12,13 +12,9 @@ 'use strict'; -declare var __DEV__: boolean; - const createDeltaClient = require('./DeltaClient/createDeltaClient'); -const deltaClient = createDeltaClient({ - hot: __DEV__, -}); +const deltaClient = createDeltaClient(); self.addEventListener('fetch', event => { const reqUrl = new URL(event.request.url); diff --git a/packages/metro/src/lib/bundle-modules/registerServiceWorker.js b/packages/metro/src/lib/bundle-modules/registerServiceWorker.js index 1c6ac39395..afb32fe3a9 100644 --- a/packages/metro/src/lib/bundle-modules/registerServiceWorker.js +++ b/packages/metro/src/lib/bundle-modules/registerServiceWorker.js @@ -9,6 +9,7 @@ */ /* eslint-env browser */ +/* eslint-disable no-console */ 'use strict'; @@ -16,16 +17,6 @@ declare var __DEV__: boolean; const injectUpdate = require('./injectUpdate'); -let info; -if (__DEV__) { - info = (...args) => { - // eslint-disable-next-line no-console - console.info(...args); - }; -} else { - info = (...args) => {}; -} - function registerServiceWorker(swUrl: string) { if ('serviceWorker' in navigator) { const sw: ServiceWorkerContainer = (navigator.serviceWorker: $FlowIssue); @@ -35,35 +26,30 @@ function registerServiceWorker(swUrl: string) { if (__DEV__) { registrationPromise.then( registration => { - info( - '[PAGE] ServiceWorker registration successful with scope: ', + console.info( + 'ServiceWorker registration successful with scope: ', registration.scope, ); }, error => { - console.error('[PAGE] ServiceWorker registration failed: ', error); + console.error('ServiceWorker registration failed: ', error); }, ); - } - sw.addEventListener('message', event => { - const messageEvent: ServiceWorkerMessageEvent = (event: $FlowIssue); - switch (messageEvent.data.type) { - case 'HMR_UPDATE': { - if (__DEV__) { - info( - '[PAGE] Received HMR update from SW: ', - messageEvent.data.body, - ); + sw.addEventListener('message', event => { + const messageEvent: ServiceWorkerMessageEvent = (event: $FlowIssue); + switch (messageEvent.data.type) { + case 'METRO_UPDATE': { + console.info('Injecting metro update:', messageEvent.data.body); + injectUpdate(messageEvent.data.update); + break; } - - injectUpdate(messageEvent.data.body); } - } - }); + }); + } }); } else if (__DEV__) { - info('[PAGE] ServiceWorker not supported'); + console.info('ServiceWorker not supported'); } }