stx-router 0.2.395 → 0.2.397
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +76 -9
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -332,6 +332,7 @@ function routerSource() {
|
|
|
332
332
|
|
|
333
333
|
var cache={};
|
|
334
334
|
var prefetching={};
|
|
335
|
+
var awaitingKey='';
|
|
335
336
|
var isNavigating=false;
|
|
336
337
|
|
|
337
338
|
// LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix
|
|
@@ -923,6 +924,19 @@ function routerSource() {
|
|
|
923
924
|
// forward-navigation page.
|
|
924
925
|
if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return Promise.resolve(false);
|
|
925
926
|
|
|
927
|
+
// Its prefetch is on the way: wait for that answer rather than asking the
|
|
928
|
+
// server a second time. A later tap elsewhere wins.
|
|
929
|
+
var awaitedKey=cacheKey(url);
|
|
930
|
+
if(o.cache&&!force&&!cache[awaitedKey]&&prefetching[awaitedKey]&&prefetching[awaitedKey].then){
|
|
931
|
+
awaitingKey=awaitedKey;
|
|
932
|
+
return prefetching[awaitedKey].then(function(){
|
|
933
|
+
if(awaitingKey!==awaitedKey)return false;
|
|
934
|
+
awaitingKey='';
|
|
935
|
+
return navigate(url,pushState,force);
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
awaitingKey='';
|
|
939
|
+
|
|
926
940
|
isNavigating=true;
|
|
927
941
|
instantNav=instantNext;
|
|
928
942
|
instantNext=false;
|
|
@@ -2211,24 +2225,70 @@ else {
|
|
|
2211
2225
|
// \u2500\u2500 Prefetch \u2500\u2500
|
|
2212
2226
|
// A link's page, fetched before it is followed into the cache navigate
|
|
2213
2227
|
// reads, so following it is a swap rather than a round trip.
|
|
2214
|
-
|
|
2228
|
+
// 'prefetching' holds each fetch in flight, so a tap that lands before its
|
|
2229
|
+
// prefetch has finished waits for that answer instead of asking again.
|
|
2230
|
+
// 'fresh' refetches a page that is cached, for an entry restored from an
|
|
2231
|
+
// earlier launch that is shown at once and replaced behind it.
|
|
2232
|
+
function prefetchLink(link,fresh){
|
|
2215
2233
|
// Same opt-out set as the click path. Without it, hovering a link the
|
|
2216
2234
|
// router is not allowed to claim still fired a real GET at it \u2014 a
|
|
2217
2235
|
// logout or OAuth URL was requested on hover alone.
|
|
2218
2236
|
if(isRouterExcluded(link))return;
|
|
2219
2237
|
var href=withCurrentLocale(link.getAttribute('href'));
|
|
2220
2238
|
var key=cacheKey(href);
|
|
2221
|
-
if(cache[key]||prefetching[key])return;
|
|
2222
|
-
|
|
2239
|
+
if((cache[key]&&!fresh)||prefetching[key])return;
|
|
2240
|
+
var eager=link.getAttribute('data-stx-prefetch')==='eager';
|
|
2223
2241
|
var wantsFragment=shouldUseFragmentResponse();
|
|
2224
|
-
fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
|
|
2242
|
+
prefetching[key]=fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
|
|
2225
2243
|
return readPrefetchResponse(r,wantsFragment);
|
|
2226
2244
|
}).then(function(result){
|
|
2227
|
-
if(result&&o.cache)
|
|
2245
|
+
if(result&&o.cache){
|
|
2246
|
+
setCache(key,result.html,result.layout,result.layoutGroup,result.title,result.containerAttrs);
|
|
2247
|
+
if(eager)keepPage(key,result);
|
|
2248
|
+
}
|
|
2228
2249
|
if(result)loadModuleRegistries(result.html,href);
|
|
2229
2250
|
}).catch(function(){}).finally(function(){delete prefetching[key]});
|
|
2230
2251
|
}
|
|
2231
2252
|
|
|
2253
|
+
// \u2500\u2500 Pages kept between launches \u2500\u2500
|
|
2254
|
+
// An eager link's page (a tab bar's) is kept in localStorage under the build
|
|
2255
|
+
// that rendered it, and put back into the cache when the next launch of the
|
|
2256
|
+
// same build starts. The first tap after opening the app is then a swap,
|
|
2257
|
+
// not a round trip, however far the server is; the eager prefetch replaces
|
|
2258
|
+
// the kept copy behind it. Pages of any other build are dropped, since a
|
|
2259
|
+
// runtime must never be handed another build's fragment (#1772).
|
|
2260
|
+
var KEPT_PREFIX='stx:pages:';
|
|
2261
|
+
var KEPT_MAX_BYTES=400000;
|
|
2262
|
+
function keptStore(){try{return window.localStorage||null}catch(e){return null}}
|
|
2263
|
+
function keepPage(key,result){
|
|
2264
|
+
var store=keptStore();
|
|
2265
|
+
if(!store||!loadedBuild||!result||!result.html||result.html.length>KEPT_MAX_BYTES)return;
|
|
2266
|
+
try{
|
|
2267
|
+
var all=JSON.parse(store.getItem(KEPT_PREFIX+loadedBuild)||'{}');
|
|
2268
|
+
all[key]={h:result.html,l:result.layout,g:result.layoutGroup,t:result.title,a:result.containerAttrs};
|
|
2269
|
+
store.setItem(KEPT_PREFIX+loadedBuild,JSON.stringify(all));
|
|
2270
|
+
}catch(e){}
|
|
2271
|
+
}
|
|
2272
|
+
function restoreKeptPages(){
|
|
2273
|
+
var store=keptStore();
|
|
2274
|
+
if(!store||!o.cache||!loadedBuild)return;
|
|
2275
|
+
try{
|
|
2276
|
+
for(var i=store.length-1;i>=0;i--){
|
|
2277
|
+
var name=store.key(i);
|
|
2278
|
+
if(name&&name.indexOf(KEPT_PREFIX)===0&&name!==KEPT_PREFIX+loadedBuild)store.removeItem(name);
|
|
2279
|
+
}
|
|
2280
|
+
var all=JSON.parse(store.getItem(KEPT_PREFIX+loadedBuild)||'{}');
|
|
2281
|
+
for(var key in all){
|
|
2282
|
+
var page=all[key];
|
|
2283
|
+
if(!Object.prototype.hasOwnProperty.call(all,key)||!page||!page.h||cache[key])continue;
|
|
2284
|
+
setCache(key,page.h,page.l||'',page.g||'',page.t||'',page.a||'');
|
|
2285
|
+
restored[key]=true;
|
|
2286
|
+
loadModuleRegistries(page.h,key);
|
|
2287
|
+
}
|
|
2288
|
+
}catch(e){}
|
|
2289
|
+
}
|
|
2290
|
+
var restored={};
|
|
2291
|
+
|
|
2232
2292
|
// A prefetched page's module registry (the file its imports are bundled
|
|
2233
2293
|
// into, one per page) is loaded with the prefetch, not on the tap. It only
|
|
2234
2294
|
// registers modules, once, so loading it early changes nothing but timing,
|
|
@@ -2255,15 +2315,21 @@ else {
|
|
|
2255
2315
|
eagerQueued=false;
|
|
2256
2316
|
var connection=navigator.connection;
|
|
2257
2317
|
if(connection&&(connection.saveData||/2g/.test(connection.effectiveType||'')))return;
|
|
2258
|
-
|
|
2318
|
+
// The page it is on too: it was loaded as a whole document, so without
|
|
2319
|
+
// its fragment the way back to it went to the network.
|
|
2259
2320
|
var links=document.querySelectorAll('[data-stx-link][data-stx-prefetch="eager"]');
|
|
2260
2321
|
for(var i=0;i<links.length;i++){
|
|
2261
2322
|
var href=links[i].getAttribute('href');
|
|
2262
|
-
if(href
|
|
2323
|
+
if(!href)continue;
|
|
2324
|
+
var key=cacheKey(withCurrentLocale(href));
|
|
2325
|
+
prefetchLink(links[i],restored[key]);
|
|
2326
|
+
delete restored[key];
|
|
2263
2327
|
}
|
|
2264
2328
|
};
|
|
2265
|
-
|
|
2266
|
-
|
|
2329
|
+
// Soon after the first paint rather than whenever the page next idles: on
|
|
2330
|
+
// a distant server each page is a second away, and the first tap comes fast.
|
|
2331
|
+
if(window.requestIdleCallback)window.requestIdleCallback(run,{timeout:500});
|
|
2332
|
+
else setTimeout(run,100);
|
|
2267
2333
|
}
|
|
2268
2334
|
|
|
2269
2335
|
if(o.prefetch){
|
|
@@ -2522,6 +2588,7 @@ else {
|
|
|
2522
2588
|
injectStyles();
|
|
2523
2589
|
injectViewTransitionCSS();
|
|
2524
2590
|
refreshCurrentLinks();
|
|
2591
|
+
restoreKeptPages();
|
|
2525
2592
|
prefetchEager();
|
|
2526
2593
|
}
|
|
2527
2594
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stx-router",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.397",
|
|
5
5
|
"description": "File-based router for STX — .stx template discovery, nested layouts, typed route params, middleware, and client-side SPA navigation.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|