Skip to content

Commit 2bd38f4

Browse files
committed
build: cache consistency in web distributions
1 parent a88c64a commit 2bd38f4

File tree

8 files changed

+176
-287
lines changed

8 files changed

+176
-287
lines changed

gulpfile.js/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ function _getFileDetails(path) {
386386
{encoding: null});
387387
return {
388388
sizeBytes: data.length,
389-
hash: crypto.createHash("md5").update(data).digest("hex")
389+
hash: crypto.createHash("sha256").update(data).digest("hex")
390390
};
391391
}
392392

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@
7171
"_patchVersionBump": "gulp patchVersionBump",
7272
"_majorVersionBump": "gulp majorVersionBump",
7373
"serve": "http-server . -p 8000 -c-1",
74-
"serveWithWebCache": "http-server ./src -p 8000 -c-1",
75-
"serveExternal": "npm run _releaseWebCache && http-server . -p 8000 -a 0.0.0.0 --log-ip true -c-1",
74+
"_serveWithWebCacheHelp": "echo !!!Make sure to npm run release:dev/stageing/prod before testing the cache!!!",
75+
"serveWithWebCache": "npm run _releaseWebCache && npm run _serveWithWebCacheHelp && http-server ./dist -p 8000 -c-1",
76+
"serveExternal": "http-server . -p 8000 -a 0.0.0.0 --log-ip true -c-1",
7677
"createJSDocs": "gulp createJSDocs && git add docs",
7778
"_translateStrings": "gulp translateStrings",
7879
"_minify": "r.js -o require.min.config.js && echo this is untested see https://stackoverflow.com/questions/14337970/minifying-requirejs-javascript-codebase-to-a-single-file"

src/assets/default-project/en/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
<div id="mainDiv">
1212
<img id="logo" alt="logo" src="images/phoenix-logo.svg" />
1313
<div id="MainText">
14-
<h1>Phoenix</h1>
15-
<span>Modern, Open-source, IDE For The Web <br /></span><br />
14+
<h1>Phoenix Code</h1>
15+
<span>Code Creatively: Visual Editing Tailored for Developers<br /></span><br />
1616
</div>
1717
<div class="video-container">
1818
<a

src/extensionsIntegrated/Phoenix/newly-added-features.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,6 @@ define(function (require, exports, module) {
5454
}, 3000);
5555
}
5656

57-
function _cacheUpdatedCB(err) {
58-
if(err) {
59-
Metrics.countEvent(Metrics.EVENT_TYPE.PLATFORM, "cache", "errorRefresh");
60-
return;
61-
}
62-
Metrics.countEvent(Metrics.EVENT_TYPE.PLATFORM, "cache",
63-
`${window.Phoenix.cache.updatePendingReloadReason}.done`);
64-
Metrics.countEvent(Metrics.EVENT_TYPE.PLATFORM, "cache", `updateCount`,
65-
window.Phoenix.cache.updatedFilesCount||0);
66-
}
67-
6857
async function _readMarkdownTextFile() {
6958
try{
7059
let markdownFile = FileSystem.getFileForPath(_getUpdateMarkdownLocalPath());
@@ -93,11 +82,5 @@ define(function (require, exports, module) {
9382
if(!Phoenix.firstBoot && !window.testEnvironment){
9483
_showNewUpdatesIfPresent();
9584
}
96-
if(!Phoenix.browser.isTauri && window.refreshServiceWorkerCache) {
97-
// window.refreshServiceWorkerCache is only present if the page loads service worker. Not available
98-
// in integ test windows as they don't load service workers.
99-
Metrics.countEvent(Metrics.EVENT_TYPE.PLATFORM, "cache", "doRefresh");
100-
window.refreshServiceWorkerCache(_cacheUpdatedCB);
101-
}
10285
};
10386
});

src/index.html

Lines changed: 78 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
<script>window.PHOENIX_APP_CACHE_VERSION="LATEST";</script>
5353
<!-- The app cache version is set by the pipeline version update script. Do not update manually.-->
5454
<script>
55+
if(["dev.phcode.dev", "staging.phcode.dev"].includes(location.hostname)
56+
&& localStorage.getItem("devDomainsEnabled") !== "true"){
57+
alert("Hello explorer, you have reached a development version of phcode.dev that is not for general use and could be very unstable." +
58+
`\n\nIf you know what you are doing and what to visit the dev site, please go to https://${location.hostname}/devEnable.html to enable dev site and visit again.` +
59+
"\n\nYou will now be redirected to phcode.dev.");
60+
window.location = "https://phcode.dev";
61+
}
5562
if(location.href.startsWith("tauri://") || location.href.startsWith('https://tauri.localhost')){
5663
const errorMessage = `You should use custom protocol phtauri:// instead of tauri protocol ${location.href} .`;
5764
alert(errorMessage);
@@ -335,90 +342,90 @@
335342
window.PhoenixBaseURL = baseUrl;
336343
}());
337344
</script>
345+
<script src="phoenix/virtualfs.js"></script>
338346
<script>
339-
if(["dev.phcode.dev", "staging.phcode.dev"].includes(location.hostname)
340-
&& localStorage.getItem("devDomainsEnabled") !== "true"){
341-
alert("Hello explorer, you have reached a development version of phcode.dev that is not for general use and could be very unstable." +
342-
`\n\nIf you know what you are doing and what to visit the dev site, please go to https://${location.hostname}/devEnable.html to enable dev site and visit again.` +
343-
"\n\nYou will now be redirected to phcode.dev.");
344-
window.location = "https://phcode.dev";
345-
}
346347
/*Bootstrap cache check. since index.html is always loaded network first, the cache reset code will always
347348
be guaranteed to be hit on app update.
348349
Note that this cannot be moved to a separate file and should be in index.html due to above reason.
349350
This is a nuke cache option. Though mostly safe, Use it wisely to prevent slow startup when resetting.
350351
*/
351-
const urlParams = new URLSearchParams(window.location.search || "");
352-
const LESS_REFRESH_SCHEDULED_KEY = "lessRefreshScheduled";
353-
const shouldRefreshLess = window.localStorage.getItem(LESS_REFRESH_SCHEDULED_KEY) === 'yes';
354-
const CACHE_NAME_EVERYTHING = "everything";
355-
function _resetCacheIfNeeded() {
356-
if(Phoenix.browser.isTauri) {
357-
// no web cache in desktop builds
358-
return;
359-
}
360-
const cacheKey = "browserCacheVersionKey";
361-
const newCacheVersion = "V16"; // just increment this number to V2, v3 etc. to force clear the cached content.
362-
const lastClearedVersion = window.localStorage.getItem(cacheKey);
363-
if(lastClearedVersion === null){
364-
// First load, no cache, return.
365-
localStorage.setItem(cacheKey, newCacheVersion);
352+
async function _resetCacheIfNeeded() {
353+
if(Phoenix.browser.isTauri || Phoenix.isTestWindow) {
354+
// no web cache in desktop builds ot test windows
366355
return;
367356
}
368357
if(!window.caches){
369-
console.error("CacheStorage Reset: API not supported by browser.");
358+
console.error("CacheStorage: API not supported by browser.");
370359
}
371-
if(lastClearedVersion !== newCacheVersion) {
372-
window.cacheRefreshInProgress = true;
373-
let openedCache;
374-
console.log("CacheStorage Reset: Reset cache in progress..");
375-
caches.open(CACHE_NAME_EVERYTHING)
376-
.then(cache => {
377-
openedCache = cache;
378-
return cache.keys();
379-
})
380-
.then(keys => {
381-
let promises = []
382-
for(let key of keys){
383-
promises.push(openedCache.delete(key));
360+
361+
const LESS_REFRESH_SCHEDULED_KEY = "lessRefreshScheduled";
362+
363+
function _readFileSafe(readPath) {
364+
return new Promise(resolve =>{
365+
fs.readFile(readPath, "utf8", (err, text)=>{
366+
if(err){
367+
console.error("Could not read", readPath)
368+
resolve(null);
369+
return;
384370
}
385-
console.log(`CacheStorage Reset: Deleting ${promises.length} cache entries from ${CACHE_NAME_EVERYTHING}`);
386-
return Promise.all(promises);
387-
}).then(() => {
388-
console.log("CacheStorage Reset: Cache successfully reset");
389-
let totalWaitTime = 0, waitTime = 100, maxWaitTime = 3000;
390-
let intervalTimer = setInterval(()=>{
391-
// wait for less to get loaded. less caches css in local storage in production urls
392-
// and might not load new css classes if we don't reset. less doesn't cache in localhost.
393-
totalWaitTime += waitTime;
394-
if(window.less && less.refresh){
395-
localStorage.setItem(cacheKey, newCacheVersion);
396-
localStorage.setItem(LESS_REFRESH_SCHEDULED_KEY, "yes");
397-
less.refresh(true).finally(()=>{
398-
localStorage.setItem(LESS_REFRESH_SCHEDULED_KEY, "no");
399-
location.reload();
400-
});
401-
clearInterval(intervalTimer);
402-
} else if(totalWaitTime > maxWaitTime){
403-
// ignore less refresh, the app load itself is critical. less refresh we will try later
404-
localStorage.setItem(cacheKey, newCacheVersion);
405-
localStorage.setItem(LESS_REFRESH_SCHEDULED_KEY, "yes");
406-
location.reload();
407-
}
408-
}, waitTime);
409-
}).catch( e => {
410-
console.error("Error while resetting cache", e);
411-
window.cacheClearError = e;
412-
window.cacheRefreshInProgress = false;
413-
// try our luck loading phoenix as cache reset failed
414-
if(window._loadPhoenixAfterSplashScreen) {
415-
window._loadPhoenixAfterSplashScreen();
371+
resolve(text);
372+
});
373+
});
374+
}
375+
376+
function _writeFileSafe(writePath, text) {
377+
return new Promise(resolve =>{
378+
fs.writeFile(writePath, text, "utf8", (err)=>{
379+
if(err){
380+
console.error("Could not written", writePath)
416381
}
417-
// throw again so that bugsnag can report if initialised. else bugsnag
418-
// will report the error when it comes online via window.cacheClearError
419-
throw e;
382+
resolve(err);
420383
});
384+
});
385+
}
386+
387+
const WEB_CACHE_FILE_PATH = "/webCacheVersion.txt";
388+
/**
389+
* Clears legacy caches from the browser storage. This function is intended to be used
390+
* when migrating to a new cache management strategy. Safe to be deleted after sep-2024
391+
* @return {Promise<void>}
392+
*/
393+
async function clearLegacyCache() {
394+
// as we switched to new versioned cache management, we will be clearing all the legacy cache.
395+
const legacyCacheName = "browserCacheVersionKey";
396+
const lastClearedVersion = window.localStorage.getItem(legacyCacheName);
397+
if(lastClearedVersion){
398+
// legacy cache detected
399+
console.log("Legacy cache detected, resetting...");
400+
localStorage.removeItem(legacyCacheName);
401+
await window.caches.delete(legacyCacheName);
402+
localStorage.setItem(LESS_REFRESH_SCHEDULED_KEY, "yes");
403+
if(window.fs){
404+
await _writeFileSafe(WEB_CACHE_FILE_PATH, PHOENIX_APP_CACHE_VERSION);
405+
location.reload();
406+
} else {
407+
location.reload();
408+
}
409+
}
421410
}
411+
await clearLegacyCache();
412+
413+
const currentCacheName = await _readFileSafe(WEB_CACHE_FILE_PATH);
414+
if(!currentCacheName) {
415+
// fresh install, no cache
416+
console.log("No cache detected, ignore if first boot or safari ITP.");
417+
await _writeFileSafe(WEB_CACHE_FILE_PATH, PHOENIX_APP_CACHE_VERSION);
418+
// on next reload, the new cache will be active and populated by service worker
419+
} else if(currentCacheName !== PHOENIX_APP_CACHE_VERSION) {
420+
// This is an upgrade we could in the future use the cacheManifest.json to increase update speed
421+
// by only selectively updating the changed files.
422+
console.log("Old version cache detected", currentCacheName, "purging and setting", PHOENIX_APP_CACHE_VERSION);
423+
await window.caches.delete(currentCacheName);
424+
await _writeFileSafe(WEB_CACHE_FILE_PATH, PHOENIX_APP_CACHE_VERSION);
425+
localStorage.setItem(LESS_REFRESH_SCHEDULED_KEY, "yes");
426+
}
427+
428+
const shouldRefreshLess = window.localStorage.getItem(LESS_REFRESH_SCHEDULED_KEY) === 'yes';
422429
if(shouldRefreshLess){
423430
let lessRefreshInterval = setInterval(()=>{
424431
// wait for less to get loaded. less caches css in local storage in production urls
@@ -475,11 +482,6 @@
475482
document.body.classList.add('dark'); // This will later be overridden by the theme manager as required.
476483
}
477484
_removeSplashScreenIfNeeded();
478-
if(window.cacheRefreshInProgress){
479-
// We should not load anything while the cache is inconsistent.
480-
// A page reload will be scheduled on successful clear.
481-
return;
482-
}
483485
var loadJS = function(url, implementationCode, location, dataMainValue){
484486
//url is URL of external file, implementationCode is the code
485487
//to be called from the file, location is the location to
@@ -505,7 +507,7 @@
505507
});
506508
} else {
507509
const interval = setInterval(()=>{
508-
if(PhStore){
510+
if(window.PhStore){
509511
clearInterval(interval);
510512
PhStore.storageReadyPromise
511513
.finally(()=>{
@@ -528,7 +530,6 @@
528530
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer
529531
-->
530532
<!-- Import the phoenix browser virtual file system -->
531-
<script src="phoenix/virtualfs.js" defer></script>
532533
<script src="phoenix/shell.js" type="module" defer></script>
533534
<script src="phoenix/virtual-server-loader.js" type="module" defer></script>
534535
<!-- node loader should come only after fs libs are loaded as we init fs libs with node fs urls-->

src/main.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ define(function (require) {
154154
try{
155155
require(["brackets"]);
156156
} catch (err) {
157-
// try a cache refresh (not a full reset). this will happen in the service worker in the background
158-
window.refreshServiceWorkerCache && window.refreshServiceWorkerCache();
157+
// try a cache reset
158+
window._resetCacheIfNeeded && window._resetCacheIfNeeded();
159159
// metrics api might not be available here as we were seeing no metrics raised. Only bugsnag there.
160160
window.logger && window.logger.reportError(err,
161161
'Critical error when loading brackets. Trying to reload again.');

src/phoenix/virtual-server-loader.js

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -98,55 +98,15 @@ if (!window.__TAURI__ && _isServiceWorkerLoaderPage() && 'serviceWorker' in navi
9898
updateViaCache: 'none'
9999
});
100100

101-
let isServerReady = false;
102-
103101
window.Phoenix.cache = {};
104102

105-
// refreshServiceWorkerCache should be done after app load to prevent mixed js script content load. Ie,
106-
// if we do the cache reset now, some scripts loaded may be from cache and some from the new version.
107-
window.refreshServiceWorkerCache = function (doneCB) {
108-
if(!isServerReady){
109-
setTimeout(()=>{
110-
window.refreshServiceWorkerCache(doneCB);
111-
}, 100);
112-
return;
113-
}
114-
logger.leaveTrail(`Service worker loader: triggering REFRESH_CACHE`);
115-
wb.messageSW({
116-
type: 'REFRESH_CACHE'
117-
}).then(({updatedFilesCount})=>{
118-
logger.leaveTrail(`Service worker loader: refresh cache updatedFilesCount: `+ updatedFilesCount);
119-
window.Phoenix.cache.updatePendingReloadReason = "refreshCache";
120-
window.Phoenix.cache.updatedFilesCount = updatedFilesCount || 0;
121-
if(updatedFilesCount){
122-
let lessRefreshInterval = setInterval(()=>{
123-
// wait for less to get loaded. less caches css in local storage in production urls
124-
// and might not load new css classes if we don't reset. less doesn't cache in localhost.
125-
if(window.less && window.less.refresh){
126-
window.less.refresh(true);
127-
clearInterval(lessRefreshInterval);
128-
}
129-
}, 500);
130-
}
131-
if(doneCB) {
132-
doneCB();
133-
}
134-
}).catch(err=>{
135-
console.error("Service worker loader: Error while triggering refresh cache", err);
136-
if(doneCB) {
137-
doneCB("REFRESH_CACHE Error");
138-
}
139-
});
140-
};
141-
142103
// Hoist service worker comm to window for everyone be able to communicate with the sw.
143104
window.messageSW = function (params) {
144105
return wb.messageSW(params);
145106
};
146107

147108
function serverReady() {
148109
console.log('Service worker loader: Server ready.');
149-
isServerReady = true;
150110
wb.messageSW({
151111
type: 'INIT_PHOENIX_CONFIG',
152112
debugMode: window.logger.logToConsolePref === 'true',

0 commit comments

Comments
 (0)