1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {featureFlags} from '../core/feature_flags'; 16 17let lastReloadDialogTime = 0; 18const kMinTimeBetweenDialogsMs = 10000; 19const changedPaths = new Set<string>(); 20 21export function initLiveReloadIfLocalhost(embeddedMode: boolean) { 22 if (!location.origin.startsWith('http://localhost:')) return; 23 if (embeddedMode) return; 24 25 const monitor = new EventSource('/live_reload'); 26 monitor.onmessage = (msg) => { 27 const change = msg.data; 28 console.log('Live reload:', change); 29 changedPaths.add(change); 30 if (change.endsWith('.css')) { 31 reloadCSS(); 32 } else if (change.endsWith('.html') || change.endsWith('.js')) { 33 reloadDelayed(); 34 } 35 }; 36 monitor.onerror = (err) => { 37 // In most cases the error is fired on reload, when the socket disconnects. 38 // Delay the error and the reconnection, so in the case of a reload we don't 39 // see any midleading message. 40 setTimeout(() => console.error('LiveReload SSE error', err), 1000); 41 }; 42} 43 44function reloadCSS() { 45 const css = document.querySelector('link[rel=stylesheet]'); 46 if (!css) return; 47 const parent = css.parentElement!; 48 parent.removeChild(css); 49 parent.appendChild(css); 50} 51 52const rapidReloadFlag = featureFlags.register({ 53 id: 'rapidReload', 54 name: 'Development: rapid live reload', 55 defaultValue: false, 56 description: 57 'During development, instantly reload the page on change. ' + 58 'Enables lower latency of live reload at the cost of potential ' + 59 'multiple re-reloads.', 60 devOnly: true, 61}); 62 63function reloadDelayed() { 64 setTimeout( 65 () => { 66 let pathsStr = ''; 67 for (const path of changedPaths) { 68 pathsStr += path + '\n'; 69 } 70 changedPaths.clear(); 71 if (Date.now() - lastReloadDialogTime < kMinTimeBetweenDialogsMs) return; 72 const reload = 73 rapidReloadFlag.get() || confirm(`${pathsStr}changed, click to reload`); 74 lastReloadDialogTime = Date.now(); 75 if (reload) { 76 window.location.reload(); 77 } 78 }, 79 rapidReloadFlag.get() ? 0 : 1000, 80 ); 81} 82