bun-plugin-stx 0.2.385 → 0.2.387
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.
|
@@ -860,6 +860,83 @@ function routerSource() {
|
|
|
860
860
|
}
|
|
861
861
|
}
|
|
862
862
|
|
|
863
|
+
// \u2500\u2500 Instant navigation: one frame, already filled \u2500\u2500
|
|
864
|
+
// A tab swap used to paint the new screen before its scripts had run: a page
|
|
865
|
+
// script with an import is a module, and a module runs after the task that
|
|
866
|
+
// inserted it, so for a few frames the screen showed its bare markup and then
|
|
867
|
+
// filled in, on every visit. Each inserted page script now reports when it
|
|
868
|
+
// has run, and an instant navigation holds the old frame (a View Transition
|
|
869
|
+
// with no animation) until they all have, capped so it can never hang.
|
|
870
|
+
var scriptsPending=0;
|
|
871
|
+
var hydrateWaiters=[];
|
|
872
|
+
window.__stxScriptRan=function(){
|
|
873
|
+
if(scriptsPending>0)scriptsPending--;
|
|
874
|
+
if(!scriptsPending){var w=hydrateWaiters;hydrateWaiters=[];w.forEach(function(f){f()})}
|
|
875
|
+
};
|
|
876
|
+
function markRan(code){scriptsPending++;return code+String.fromCharCode(10)+';window.__stxScriptRan&&window.__stxScriptRan();'}
|
|
877
|
+
function whenHydrated(capMs){
|
|
878
|
+
return new Promise(function(resolve){
|
|
879
|
+
var done=false;
|
|
880
|
+
function finish(){if(done)return;done=true;setTimeout(resolve,0)}
|
|
881
|
+
if(!scriptsPending){done=true;resolve();return}
|
|
882
|
+
hydrateWaiters.push(finish);
|
|
883
|
+
setTimeout(finish,capMs);
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
// Whether a fragment's scripts all run inside the swap's own task: none is
|
|
887
|
+
// a module, and every file it names is already loaded. Then an instant
|
|
888
|
+
// navigation needs no held frame at all: swapped, run and bound before the
|
|
889
|
+
// browser next paints, it shows the new screen whole in the first frame.
|
|
890
|
+
function swapRunsInOneTask(html,base){
|
|
891
|
+
var sync=true;
|
|
892
|
+
html.replace(new RegExp('<scr'+'ipt\\\\b([^>]*)>([\\\\s\\\\S]*?)<\\\\/scr'+'ipt>','gi'),function(m,attrs,code){
|
|
893
|
+
if(!sync)return m;
|
|
894
|
+
var typeMatch=attrs.match(/\\btype\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))/i);
|
|
895
|
+
var scriptType=((typeMatch&&(typeMatch[1]||typeMatch[2]||typeMatch[3]))||'').trim().toLowerCase();
|
|
896
|
+
if(scriptType==='module'){sync=false;return m}
|
|
897
|
+
if(scriptType&&scriptType!=='text/javascript'&&scriptType!=='application/javascript')return m;
|
|
898
|
+
var srcMatch=attrs.match(/\\bsrc\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))/i);
|
|
899
|
+
var src=srcMatch&&(srcMatch[1]||srcMatch[2]||srcMatch[3]);
|
|
900
|
+
if(src){
|
|
901
|
+
if(!isSharedClientScriptAttrs(attrs)){var key=externalScriptKey(src,base);if(key&&!loadedExternalScripts[key])sync=false}
|
|
902
|
+
return m;
|
|
903
|
+
}
|
|
904
|
+
if(code&&hasStaticImport(code))sync=false;
|
|
905
|
+
return m;
|
|
906
|
+
});
|
|
907
|
+
return sync;
|
|
908
|
+
}
|
|
909
|
+
function runInOneTask(complete){
|
|
910
|
+
// The runtime binds on stx:load after a debounce; bind now, in this task.
|
|
911
|
+
function flush(){if(window.stx&&typeof window.stx._flushLoad==='function')window.stx._flushLoad()}
|
|
912
|
+
window.addEventListener('stx:load',flush,{once:true});
|
|
913
|
+
try{complete()}finally{window.removeEventListener('stx:load',flush)}
|
|
914
|
+
}
|
|
915
|
+
function runInstantSwap(complete){
|
|
916
|
+
if(!document.startViewTransition)return false;
|
|
917
|
+
var root=document.documentElement;
|
|
918
|
+
root.classList.add('stx-instant');
|
|
919
|
+
var clear=function(){root.classList.remove('stx-instant')};
|
|
920
|
+
try{
|
|
921
|
+
var transition=document.startViewTransition(function(){
|
|
922
|
+
scriptsPending=0;
|
|
923
|
+
// The runtime binds the page on stx:load after a short debounce; run
|
|
924
|
+
// it now, so the frame this reveals is bound rather than showing its
|
|
925
|
+
// {{ }} for a frame before they fill in.
|
|
926
|
+
var loaded=new Promise(function(resolve){window.addEventListener('stx:load',function(){if(window.stx&&typeof window.stx._flushLoad==='function')window.stx._flushLoad();resolve()},{once:true});setTimeout(resolve,300)});
|
|
927
|
+
complete();
|
|
928
|
+
return loaded.then(function(){return whenHydrated(300)});
|
|
929
|
+
});
|
|
930
|
+
if(transition&&transition.finished&&transition.finished.then)transition.finished.then(clear,clear);else clear();
|
|
931
|
+
if(transition&&transition.ready&&transition.ready.catch)transition.ready.catch(function(){});
|
|
932
|
+
return true;
|
|
933
|
+
}catch(err){
|
|
934
|
+
clear();
|
|
935
|
+
log('[router] instant transition unavailable:',err&&err.message?err.message:err);
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
863
940
|
// \u2500\u2500 Progress bar (0\u2192100% at top of viewport) \u2500\u2500
|
|
864
941
|
// Native loading indicator baked into the router. Starts when navigation
|
|
865
942
|
// begins, trickles up to ~90% during fetch, snaps to 100% + fades on
|
|
@@ -1457,9 +1534,10 @@ function routerSource() {
|
|
|
1457
1534
|
function tokenState(){var state={};state[SCROLL_TOKEN]=scrollToken;return state}
|
|
1458
1535
|
|
|
1459
1536
|
// Second arg accepts the legacy pushState boolean OR an options object
|
|
1460
|
-
// { replace }. Callers inside this file still pass the boolean.
|
|
1537
|
+
// { replace, instant }. Callers inside this file still pass the boolean.
|
|
1538
|
+
// instant is what data-stx-transition="none" is to a link: no cross-fade.
|
|
1461
1539
|
function navigate(url,pushState,force){
|
|
1462
|
-
if(pushState&&typeof pushState==='object')pushState=pushState.replace?'replace':true
|
|
1540
|
+
if(pushState&&typeof pushState==='object'){if(pushState.instant)instantNext=true;pushState=pushState.replace?'replace':true}
|
|
1463
1541
|
// Lang-picker passes force=true with an already-localized path (/en/...).
|
|
1464
1542
|
// Re-localizing would map it back to the *current* locale and no-op.
|
|
1465
1543
|
if(!force) url=withCurrentLocale(url);
|
|
@@ -1985,7 +2063,7 @@ else {
|
|
|
1985
2063
|
if(hasImport){
|
|
1986
2064
|
ns.type='module';
|
|
1987
2065
|
}
|
|
1988
|
-
ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
|
|
2066
|
+
ns.textContent=markRan((hasImport||isAlreadyScoped)?code:'{'+code+'}');
|
|
1989
2067
|
ns.setAttribute('data-stx-page','');
|
|
1990
2068
|
if(placeholder&&placeholder.parentNode){
|
|
1991
2069
|
ns.setAttribute('data-stx-positioned','');
|
|
@@ -2026,7 +2104,9 @@ else {
|
|
|
2026
2104
|
resolve(true);
|
|
2027
2105
|
}catch(err){reject(err)}
|
|
2028
2106
|
}
|
|
2029
|
-
if(
|
|
2107
|
+
if(instantNav&&swapRunsInOneTask(html,url)){runInOneTask(completeFragSwap)}
|
|
2108
|
+
else if(instantNav&&runInstantSwap(completeFragSwap)){}
|
|
2109
|
+
else if(runViewTransition(completeFragSwap)){}
|
|
2030
2110
|
else if(instantNav){completeFragSwap()}
|
|
2031
2111
|
else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){completeFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}
|
|
2032
2112
|
});
|
|
@@ -2442,7 +2522,7 @@ else {
|
|
|
2442
2522
|
// ESM, and top-level 'import' is illegal inside a block, which
|
|
2443
2523
|
// would throw SyntaxError before the script ever runs.
|
|
2444
2524
|
var alreadyScoped=runsAlways(typeof entry==='string'?'':entry.run,text);
|
|
2445
|
-
ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+republishTopLevel(text)+'}';
|
|
2525
|
+
ns.textContent=markRan((hasImport||alreadyScoped)?text:'{'+text+republishTopLevel(text)+'}');
|
|
2446
2526
|
ns.setAttribute('data-stx-page','');
|
|
2447
2527
|
var placeholder=entry.slot?qs('script[data-stx-route-script="'+entry.slot+'"]'):null;
|
|
2448
2528
|
if(placeholder&&placeholder.parentNode){
|
|
@@ -2476,7 +2556,9 @@ else {
|
|
|
2476
2556
|
resolve(true);
|
|
2477
2557
|
}catch(err){reject(err)}
|
|
2478
2558
|
}
|
|
2479
|
-
if(
|
|
2559
|
+
if(instantNav&&runInstantSwap(completeSwap)){
|
|
2560
|
+
}
|
|
2561
|
+
else if(runViewTransition(completeSwap)){
|
|
2480
2562
|
}
|
|
2481
2563
|
else if(instantNav){
|
|
2482
2564
|
// A tab: swap at once, no fade either way.
|
|
@@ -2771,9 +2853,25 @@ else {
|
|
|
2771
2853
|
return readPrefetchResponse(r,wantsFragment);
|
|
2772
2854
|
}).then(function(result){
|
|
2773
2855
|
if(result&&o.cache)setCache(key,result.html,result.layout,result.layoutGroup,result.title,result.containerAttrs);
|
|
2856
|
+
if(result)loadModuleRegistries(result.html,href);
|
|
2774
2857
|
}).catch(function(){}).finally(function(){delete prefetching[key]});
|
|
2775
2858
|
}
|
|
2776
2859
|
|
|
2860
|
+
// A prefetched page's module registry (the file its imports are bundled
|
|
2861
|
+
// into, one per page) is loaded with the prefetch, not on the tap. It only
|
|
2862
|
+
// registers modules, once, so loading it early changes nothing but timing,
|
|
2863
|
+
// and the tap then finds every script it needs already here: the swap runs
|
|
2864
|
+
// in one task and the first visit to a tab paints whole, like the second.
|
|
2865
|
+
function loadModuleRegistries(html,base){
|
|
2866
|
+
html.replace(new RegExp('<scr'+'ipt\\\\b([^>]*)>','gi'),function(m,attrs){
|
|
2867
|
+
if(!/(?:^|\\s)data-stx-modules(?:[\\s=]|$)/i.test(attrs))return m;
|
|
2868
|
+
var srcMatch=attrs.match(/\\bsrc\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))/i);
|
|
2869
|
+
var src=srcMatch&&(srcMatch[1]||srcMatch[2]||srcMatch[3]);
|
|
2870
|
+
if(src)loadExternalScript(src,base);
|
|
2871
|
+
return m;
|
|
2872
|
+
});
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2777
2875
|
// Links marked data-stx-prefetch="eager", fetched once the page is idle, so
|
|
2778
2876
|
// even their first tap is served from the cache: the way a phone's tab bar
|
|
2779
2877
|
// switches instantly. Not on a connection that asked to save data.
|
|
@@ -2968,7 +3066,7 @@ else {
|
|
|
2968
3066
|
// The container the router focuses after a navigation is not a
|
|
2969
3067
|
// control, so it draws no focus ring: Safari drew one around the whole
|
|
2970
3068
|
// page, a blue line along its bottom edge on every screen.
|
|
2971
|
-
var css='[data-stx-route-focus]:focus{outline:none}.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}';
|
|
3069
|
+
var css='[data-stx-route-focus]:focus{outline:none}.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}html.stx-instant::view-transition-group(*),html.stx-instant::view-transition-old(*),html.stx-instant::view-transition-new(*){animation:none!important}';
|
|
2972
3070
|
if(o.viewTransitions&&'startViewTransition' in document){
|
|
2973
3071
|
var dur=(o.viewTransitionDuration||220)+'ms';
|
|
2974
3072
|
var ease=o.viewTransitionEasing||'cubic-bezier(0.16, 1, 0.3, 1)';
|
package/dist/serve.js
CHANGED
|
@@ -9800,7 +9800,7 @@ function __stxOverlay(errs){
|
|
|
9800
9800
|
rebuildDiscoveredPagePaths(kept);
|
|
9801
9801
|
await indexPageMiddleware(kept);
|
|
9802
9802
|
try {
|
|
9803
|
-
const { Router } = await import("./chunk-
|
|
9803
|
+
const { Router } = await import("./chunk-2fh2t5gw.js");
|
|
9804
9804
|
const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
|
|
9805
9805
|
const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName(), exclude: options.exclude });
|
|
9806
9806
|
if (!options.quiet)
|
|
@@ -10457,7 +10457,7 @@ ${block}`;
|
|
|
10457
10457
|
_server = bunServe({
|
|
10458
10458
|
port: actualPort,
|
|
10459
10459
|
reusePort: options.reusePort ?? false,
|
|
10460
|
-
idleTimeout: production ?
|
|
10460
|
+
idleTimeout: production ? 120 : 0,
|
|
10461
10461
|
async fetch(req, server) {
|
|
10462
10462
|
const warmup = awaitImageWarmup();
|
|
10463
10463
|
if (warmup)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-plugin-stx",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.387",
|
|
5
5
|
"description": "A Bun plugin that allows for using Laravel Blade-like syntax.",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.org>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"typecheck": "bun tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@stacksjs/stx": "0.2.
|
|
78
|
-
"stx-router": "0.2.
|
|
77
|
+
"@stacksjs/stx": "0.2.387",
|
|
78
|
+
"stx-router": "0.2.387"
|
|
79
79
|
}
|
|
80
80
|
}
|