In live reloading, also reload if the server is being recycled. Plus rewrite LiveReloading.ts completely to be better organized.

This commit is contained in:
Steve Sanderson 2018-04-04 14:06:30 +01:00
parent de4d78f454
commit f1283940b7
1 changed files with 72 additions and 28 deletions

View File

@ -1,38 +1,78 @@
export function enableLiveReloading(endpointUri: string) {
listenForReloadEvent(endpointUri);
const pollIntervalMs = 500;
const maxPollDurationMs = 10 * 1000;
export function enableLiveReloading(endpointUri: string) {
new ReloadContext(endpointUri).start();
}
function listenForReloadEvent(endpointUri: string) {
if (!WebSocket) {
console.log('Browser does not support WebSocket, so live reloading will be disabled.');
return;
class ReloadContext {
private _websocketUri: string;
private _didConnect = false;
private _pollUntilConnectedThenReload = false;
private _stopPollingAtTime: Date | null = null;
constructor(endpointUri: string) {
this._websocketUri = toAbsoluteWebSocketUri(endpointUri);
}
// First, connect to the endpoint
const websocketUri = toAbsoluteWebSocketUri(endpointUri);
const source = new WebSocket(websocketUri);
let allowConnectionFailedErrorReporting = true;
source.onopen = e => {
allowConnectionFailedErrorReporting = false;
};
source.onerror = e => {
if (allowConnectionFailedErrorReporting) {
allowConnectionFailedErrorReporting = false;
console.error(`The client app was compiled with live reloading enabled, but could not open `
+ ` a WebSocket connection to the server at ${websocketUri}\n`
+ `To fix this inconsistency, either run the server in development mode, or compile the `
+ `client app in Release configuration.`);
start() {
if (typeof WebSocket !== 'undefined') {
this._attemptToConnect(/* delay */ 0);
} else {
console.log('Browser does not support WebSocket, so live reloading will be disabled.');
}
};
}
// If we're notified that we should reload, then do so
source.onmessage = e => {
if (e.data === 'reload') {
location.reload();
private _attemptToConnect(delayMs: number) {
setTimeout(() => {
const source = new WebSocket(this._websocketUri);
source.onopen = event => this._onOpen();
source.onmessage = event => this._onMessage(event.data);
source.onerror = event => this._onError();
source.onclose = event => this._onClose();
}, delayMs);
}
private _onOpen() {
this._didConnect = true;
if (this._pollUntilConnectedThenReload) {
reloadNow();
}
};
}
private _onMessage(data: string) {
if (data === 'reload') {
reloadNow();
}
}
private _onClose() {
if (this._didConnect) {
// Looks like the server is being recycled (or possibly just shut down, but in the
// absence of a graceful shutdown we have no way to tell the difference)
// Wait until the server appears to be back, then reload
this._pollUntilConnectedThenReload = true;
this._stopPollingAtTime = new Date(new Date().valueOf() + maxPollDurationMs);
this._attemptToConnect(/* delay */ pollIntervalMs);
}
}
private _onError() {
if (!this._didConnect) {
if (this._pollUntilConnectedThenReload) {
if (new Date() < this._stopPollingAtTime!) {
// Continue polling
this._attemptToConnect(/* delay */ pollIntervalMs);
}
} else {
console.error(`The client app was compiled with live reloading enabled, but could not open `
+ ` a WebSocket connection to the server at ${this._websocketUri}\n`
+ `To fix this inconsistency, either run the server in development mode, or compile the `
+ `client app in Release configuration.`);
}
}
}
}
function toAbsoluteWebSocketUri(uri: string) {
@ -46,3 +86,7 @@ function toAbsoluteWebSocketUri(uri: string) {
// Scheme must be ws: or wss:
return uri.replace(/^http/, 'ws');
}
function reloadNow() {
location.reload();
}