From a69fea097f75d5d4685c1dd8723190349f878b40 Mon Sep 17 00:00:00 2001 From: Alexandre Kirszenberg Date: Mon, 29 Oct 2018 08:54:18 -0700 Subject: [PATCH] Add onUpdateStart and onUpdateError Reviewed By: mjesun Differential Revision: D10527995 fbshipit-source-id: fc0209282e535101eae5257774d33f57de8d71d3 --- .../src/HmrServer/__tests__/HmrServer-test.js | 1 + .../__tests__/createDeltaClient-test.js | 143 ++++++++++++++++++ .../DeltaClient/createDeltaClient.js | 37 ++++- .../bundle-modules/registerServiceWorker.js | 10 +- 4 files changed, 189 insertions(+), 2 deletions(-) diff --git a/packages/metro/src/HmrServer/__tests__/HmrServer-test.js b/packages/metro/src/HmrServer/__tests__/HmrServer-test.js index dc63062310..7805fb04f0 100644 --- a/packages/metro/src/HmrServer/__tests__/HmrServer-test.js +++ b/packages/metro/src/HmrServer/__tests__/HmrServer-test.js @@ -210,6 +210,7 @@ describe('HmrServer', () => { }, ]); }); + it('should return the correctly formatted HMR message after a file change', async () => { const sendMessage = jest.fn(); 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 e046be1717..12ee58da48 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 @@ -347,6 +347,89 @@ post(\\"rev0\\");" }); }); + it('sends an update start message to clients', async () => { + const clientMock = { + postMessage: jest.fn(), + }; + global.clients = { + get: jest.fn().mockResolvedValue(clientMock), + }; + const deltaClient = createDeltaClient(); + + deltaClient({ + clientId: 'client0', + request: new Request('https://localhost/bundles/cool.bundle'), + }); + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], + deleted: [], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + emit('update-start'); + + expect(global.clients.get).toHaveBeenCalledWith('client0'); + + await flushPromises(); + + expect(clientMock.postMessage).toHaveBeenCalledWith({ + type: 'METRO_UPDATE_START', + }); + }); + + it('sends an update error message to clients', async () => { + const clientMock = { + postMessage: jest.fn(), + }; + global.clients = { + get: jest.fn().mockResolvedValue(clientMock), + }; + const deltaClient = createDeltaClient(); + + deltaClient({ + clientId: 'client0', + request: new Request('https://localhost/bundles/cool.bundle'), + }); + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], + deleted: [], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + const error = { + type: 'CompleteFailureError', + message: 'Everything went south', + errors: [], + }; + emit('update-start'); + emit('error', error); + + expect(global.clients.get).toHaveBeenCalledWith('client0'); + + await flushPromises(); + + expect(clientMock.postMessage).toHaveBeenCalledWith({ + type: 'METRO_UPDATE_ERROR', + error, + }); + }); + it('patches the cached bundle on later update', async () => { const deltaClient = createDeltaClient(); @@ -424,6 +507,66 @@ post(\\"rev0\\");" expect(onUpdate).toHaveBeenCalledWith('client0', update); }); + it('accepts a custom onUpdateStart function', async () => { + const onUpdateStart = jest.fn(); + const deltaClient = createDeltaClient({onUpdateStart}); + + deltaClient({ + clientId: 'client0', + request: new Request('https://localhost/bundles/cool.bundle'), + }); + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], + deleted: [], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + emit('update-start'); + + expect(onUpdateStart).toHaveBeenCalledWith('client0'); + }); + + it('accepts a custom onUpdateError function', async () => { + const onUpdateError = jest.fn(); + const deltaClient = createDeltaClient({onUpdateError}); + + deltaClient({ + clientId: 'client0', + request: new Request('https://localhost/bundles/cool.bundle'), + }); + + await flushPromises(); + + emit('open'); + emit('update-start'); + emit('update', { + revisionId: 'rev0', + modules: [], + deleted: [], + sourceMappingURLs: [], + sourceURLs: [], + }); + emit('update-done'); + + const error = { + type: 'CompleteFailureError', + message: 'Everything went south', + errors: [], + }; + emit('update-start'); + emit('error', error); + + expect(onUpdateError).toHaveBeenCalledWith('client0', error); + }); + it('only connects once for a given revisionId', async () => { const onUpdate = jest.fn(); const deltaClient = createDeltaClient({onUpdate}); diff --git a/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js b/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js index e0724c903c..72167c4319 100644 --- a/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js +++ b/packages/metro/src/lib/bundle-modules/DeltaClient/createDeltaClient.js @@ -19,7 +19,12 @@ const bundleToString = require('./bundleToString'); const patchBundle = require('./patchBundle'); const stringToBundle = require('./stringToBundle'); -import type {Bundle, DeltaBundle, HmrUpdate} from '../types.flow'; +import type { + Bundle, + DeltaBundle, + HmrUpdate, + FormattedError, +} from '../types.flow'; declare var __DEV__: boolean; @@ -36,7 +41,9 @@ export type GetHmrServerUrl = ( export type DeltaClientOptions = {| +getDeltaBundle?: GetDeltaBundle, +getHmrServerUrl?: GetHmrServerUrl, + +onUpdateStart?: (clientId: string) => void, +onUpdate?: (clientId: string, update: HmrUpdate) => void, + +onUpdateError?: (clientId: string, error: FormattedError) => void, |}; export type DeltaClient = (event: FetchEvent) => Promise; @@ -82,10 +89,33 @@ function defaultOnUpdate(clientId: string, update: HmrUpdate) { }); } +function defaultOnUpdateStart(clientId: string) { + clients.get(clientId).then(client => { + if (client != null) { + client.postMessage({ + type: 'METRO_UPDATE_START', + }); + } + }); +} + +function defaultOnUpdateError(clientId: string, error: FormattedError) { + clients.get(clientId).then(client => { + if (client != null) { + client.postMessage({ + type: 'METRO_UPDATE_ERROR', + error, + }); + } + }); +} + function createDeltaClient({ getHmrServerUrl = defaultGetHmrServerUrl, getDeltaBundle = defaultGetDeltaBundle, + onUpdateStart = defaultOnUpdateStart, onUpdate = defaultOnUpdate, + onUpdateError = defaultOnUpdateError, }: DeltaClientOptions = {}): DeltaClient { const clientsByRevId: Map> = new Map(); @@ -140,6 +170,11 @@ function createDeltaClient({ reject(error); return; } + clientIds.forEach(clientId => onUpdateError(clientId, error)); + }); + + wsClient.on('update-start', () => { + clientIds.forEach(clientId => onUpdateStart(clientId)); }); wsClient.on('update', update => { diff --git a/packages/metro/src/lib/bundle-modules/registerServiceWorker.js b/packages/metro/src/lib/bundle-modules/registerServiceWorker.js index afb32fe3a9..ff780ede4c 100644 --- a/packages/metro/src/lib/bundle-modules/registerServiceWorker.js +++ b/packages/metro/src/lib/bundle-modules/registerServiceWorker.js @@ -39,9 +39,17 @@ function registerServiceWorker(swUrl: string) { sw.addEventListener('message', event => { const messageEvent: ServiceWorkerMessageEvent = (event: $FlowIssue); switch (messageEvent.data.type) { + case 'METRO_UPDATE_START': { + console.info('Metro update started.'); + break; + } case 'METRO_UPDATE': { console.info('Injecting metro update:', messageEvent.data.body); - injectUpdate(messageEvent.data.update); + injectUpdate(messageEvent.data.body); + break; + } + case 'METRO_UPDATE_ERROR': { + console.error('Metro update error: ', messageEvent.data.error); break; } }