bun-plugin-stx 0.2.337 → 0.2.339

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.
@@ -651,6 +651,51 @@ async function runMiddleware(middlewareNames, context) {
651
651
  }
652
652
  // ../router/src/client.ts
653
653
  var cachedRouterScript;
654
+ var cachedRouterScriptDev;
655
+ function stripRouterLogs(source) {
656
+ const out = [];
657
+ const needle = "log(";
658
+ let i = 0;
659
+ while (i < source.length) {
660
+ const hit = source.indexOf(needle, i);
661
+ if (hit === -1) {
662
+ out.push(source.slice(i));
663
+ break;
664
+ }
665
+ const before = hit > 0 ? source[hit - 1] : " ";
666
+ const isDeclaration = /\bfunction\s+$/.test(source.slice(Math.max(0, hit - 16), hit));
667
+ if (/[\w$.]/.test(before) || isDeclaration) {
668
+ out.push(source.slice(i, hit + needle.length));
669
+ i = hit + needle.length;
670
+ continue;
671
+ }
672
+ out.push(source.slice(i, hit));
673
+ let depth = 1;
674
+ let j = hit + needle.length;
675
+ while (j < source.length && depth > 0) {
676
+ const character = source[j];
677
+ if (character === '"' || character === "'" || character === "`") {
678
+ const quote = character;
679
+ j++;
680
+ while (j < source.length && source[j] !== quote) {
681
+ if (source[j] === "\\")
682
+ j++;
683
+ j++;
684
+ }
685
+ j++;
686
+ continue;
687
+ }
688
+ if (character === "(")
689
+ depth++;
690
+ else if (character === ")")
691
+ depth--;
692
+ j++;
693
+ }
694
+ out.push("0");
695
+ i = j;
696
+ }
697
+ return out.join("");
698
+ }
654
699
  function minifyRouterScript(source) {
655
700
  try {
656
701
  const transpiler = new Bun.Transpiler({ loader: "js", minifyWhitespace: true });
@@ -659,22 +704,109 @@ function minifyRouterScript(source) {
659
704
  return source;
660
705
  }
661
706
  }
662
- function getRouterScript() {
663
- if (cachedRouterScript)
664
- return cachedRouterScript;
665
- const source = `
707
+ function routerSource() {
708
+ return `
666
709
  ;(function(){
667
710
  'use strict';
668
711
  var ROUTER_REV=7;
669
712
  if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;
670
713
 
671
714
  // \u2500\u2500 Configuration \u2500\u2500
672
- var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50,routeFocus:true,announceRoute:true,interceptForms:false,cssLoadTimeout:1500};
715
+ var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50,routeFocus:true,announceRoute:true,interceptForms:false,cssLoadTimeout:1500,scrollRestoration:true};
673
716
  var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
674
717
  var containerSel=o.container;
675
718
  var debug=!!o.debug;
676
719
  function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}
677
720
 
721
+ // Shorthands for the document-rooted queries this script makes ~33 times.
722
+ // Every page carries these bytes, and the budget in
723
+ // scripts/performance-budgets.ts is what noticed.
724
+ var LAYOUT_META='meta[name="stx-layout"]';
725
+ var LAYOUT_GROUP_META='meta[name="stx-layout-group"]';
726
+ var BUILD_META='meta[name="stx-build"]';
727
+ function qs(sel){return document.querySelector(sel)}
728
+ function qsa(sel){return document.querySelectorAll(sel)}
729
+ function ce(tag){return document.createElement(tag)}
730
+ function dhead(){return document.head}
731
+
732
+ // \u2500\u2500 Scroll position \u2500\u2500
733
+ // Forward navigation goes to the top, or to the hash. Back and forward return
734
+ // to where the entry was left -- the browser cannot do that for us, because
735
+ // by the time it would restore, the content of the entry has not been fetched
736
+ // yet, so it clamps to whatever height the outgoing page happened to have.
737
+ // history.scrollRestoration therefore goes to manual and the positions are
738
+ // kept here, keyed by a token written into each history entry (two entries
739
+ // for the same URL keep their own positions, which is the point).
740
+ //
741
+ // Manual restoration also takes the browser's reload behaviour away, so the
742
+ // positions are mirrored into sessionStorage: a reload, or a return to the
743
+ // tab, finds the entry's token already in history.state and restores from
744
+ // there. sessionStorage is per tab and dies with it, same as the history.
745
+ var SCROLL_TOKEN='__stxScroll';
746
+ var SCROLL_STORE='stx:scroll:';
747
+ var scrollSeq=0;
748
+ var pendingScroll=null;
749
+ var restoreScroll=!!o.scrollRestoration;
750
+ function newScrollToken(){return 's'+(++scrollSeq)+'-'+Date.now()}
751
+ var scrollToken=(history.state&&history.state[SCROLL_TOKEN])||newScrollToken();
752
+ // sessionStorage is the only store: it is synchronous, it is per tab, it dies
753
+ // with the tab exactly as the history does, and it is the one that survives a
754
+ // reload. A private window that refuses it degrades to scrolling to the top.
755
+ function readScroll(token){
756
+ try{
757
+ var parts=(window.sessionStorage.getItem(SCROLL_STORE+token)||'').split(',');
758
+ return parts.length===2?[parseFloat(parts[0])||0,parseFloat(parts[1])||0]:null;
759
+ }catch(e){return null}
760
+ }
761
+ // Called while the outgoing entry is still the current one, so the position
762
+ // read here belongs to the token being written.
763
+ function rememberScroll(){
764
+ if(!restoreScroll)return;
765
+ try{window.sessionStorage.setItem(SCROLL_STORE+scrollToken,(window.pageXOffset||window.scrollX||0)+','+(window.pageYOffset||window.scrollY||0))}catch(e){}
766
+ }
767
+ // One place decides where a swap leaves the viewport, so the fragment path
768
+ // and the whole-document path cannot drift apart.
769
+ function applyScroll(hash){
770
+ if(pendingScroll){
771
+ var at=pendingScroll;
772
+ pendingScroll=null;
773
+ window.scrollTo({left:at[0],top:at[1],behavior:'instant'});
774
+ return;
775
+ }
776
+ if(o.scrollToTop&&!hash){window.scrollTo({top:0,behavior:'instant'});return}
777
+ if(hash){var el=qs(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
778
+ }
779
+ // Keeps whatever another script put in the entry; only adds the token.
780
+ function stampScrollToken(){
781
+ var state={};
782
+ var current=history.state;
783
+ if(current&&typeof current==='object')for(var key in current)state[key]=current[key];
784
+ state[SCROLL_TOKEN]=scrollToken;
785
+ try{history.replaceState(state,'',location.href)}catch(e){}
786
+ }
787
+ if(restoreScroll){
788
+ try{history.scrollRestoration='manual'}catch(e){}
789
+ // A token already in the entry means this document is a reload or a
790
+ // restore of an entry visited before, so its position is worth asking for.
791
+ var known=history.state&&history.state[SCROLL_TOKEN];
792
+ if(known){
793
+ var saved=readScroll(scrollToken);
794
+ if(saved&&(saved[0]||saved[1])){
795
+ var restoreAt=function(){window.scrollTo({left:saved[0],top:saved[1],behavior:'instant'})};
796
+ if(document.readyState==='complete')restoreAt();
797
+ else window.addEventListener('load',restoreAt,{once:true});
798
+ }
799
+ }
800
+ else{
801
+ stampScrollToken();
802
+ }
803
+ // A reload or a link out of the app never calls writeHistory, so this is
804
+ // the only chance to record where the page was left. pagehide fires for
805
+ // both, and unlike beforeunload it does not keep the page out of the
806
+ // back/forward cache.
807
+ window.addEventListener('pagehide',rememberScroll);
808
+ }
809
+
678
810
  // Replace, never merge: absent payloads must forget the outgoing page's data.
679
811
  // Run at the committed swap, not during prefetch or a superseded navigation.
680
812
  function hydrateServerData(html){
@@ -697,7 +829,7 @@ function getRouterScript() {
697
829
  // Conservative on purpose: act only when BOTH ids are known. A missing id is
698
830
  // "no information", never "mismatch", so statically hosted output (no headers)
699
831
  // and older servers behave exactly as before.
700
- var buildMeta=document.querySelector('meta[name="stx-build"]');
832
+ var buildMeta=qs(BUILD_META);
701
833
  var loadedBuild=buildMeta?(buildMeta.getAttribute('content')||''):'';
702
834
  window.__stxBuild=loadedBuild;
703
835
  function isBuildSkew(incoming){
@@ -845,7 +977,7 @@ function getRouterScript() {
845
977
  function parseContainerAttrs(attrStr){
846
978
  var map={};
847
979
  if(!attrStr)return map;
848
- var probe=document.createElement('div');
980
+ var probe=ce('div');
849
981
  probe.innerHTML='<i '+attrStr+'></i>';
850
982
  var src=probe.firstChild;
851
983
  // getAttributeNames first: it is the same standard DOM answer and it is
@@ -934,7 +1066,7 @@ function getRouterScript() {
934
1066
  }
935
1067
 
936
1068
  function getContainer(){
937
- return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');
1069
+ return qs(containerSel)||qs('[data-stx-content]')||qs('main');
938
1070
  }
939
1071
 
940
1072
  // \u2500\u2500 Navigation \u2500\u2500
@@ -1018,7 +1150,7 @@ function getRouterScript() {
1018
1150
  // navigation. Scripts without an owner are unaffected.
1019
1151
  function ownerStays(owner){
1020
1152
  if(!owner)return false;
1021
- var root=document.querySelector('[data-stx-scope="'+owner+'"]');
1153
+ var root=qs('[data-stx-scope="'+owner+'"]');
1022
1154
  return !root||typeof root.__stx_disposers==='function';
1023
1155
  }
1024
1156
  function hashScript(code){
@@ -1185,7 +1317,7 @@ function getRouterScript() {
1185
1317
  if(!key||loadedExternalScripts[key])return null;
1186
1318
  loadedExternalScripts[key]=1;
1187
1319
  return new Promise(function(resolve){
1188
- var el=document.createElement('script');
1320
+ var el=ce('script');
1189
1321
  el.src=key;
1190
1322
  // Deliberately NOT data-stx-page: both swap paths clear those on every
1191
1323
  // navigation, which would sweep this away and \u2014 since it is never loaded
@@ -1194,12 +1326,12 @@ function getRouterScript() {
1194
1326
  el.setAttribute('data-stx-external','');
1195
1327
  el.onload=function(){resolve()};
1196
1328
  el.onerror=function(){log('[router] container script failed:',key);resolve()};
1197
- document.head.appendChild(el);
1329
+ dhead().appendChild(el);
1198
1330
  });
1199
1331
  }
1200
1332
 
1201
1333
  // Record scripts from the initial page load
1202
- document.querySelectorAll('script').forEach(function(s){
1334
+ qsa('script').forEach(function(s){
1203
1335
  var text=s.textContent||'';
1204
1336
  if(s.hasAttribute('src'))rememberExternalScript(s.getAttribute('src'));
1205
1337
  else if(text.trim())executedScriptHashes[hashScript(text)]=1;
@@ -1226,17 +1358,17 @@ function getRouterScript() {
1226
1358
  return part?part.replace(/\\.stx$/i,''):'app';
1227
1359
  }
1228
1360
  function getCurrentLayoutGroup(){
1229
- var meta=document.querySelector('meta[name="stx-layout-group"]');
1361
+ var meta=qs(LAYOUT_GROUP_META);
1230
1362
  if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');
1231
- var layout=document.querySelector('meta[name="stx-layout"]');
1363
+ var layout=qs(LAYOUT_META);
1232
1364
  return defaultLayoutGroup(layout?layout.getAttribute('content'):'');
1233
1365
  }
1234
1366
  function getDocLayoutGroup(doc,layout){
1235
- var meta=doc&&doc.querySelector?doc.querySelector('meta[name="stx-layout-group"]'):null;
1367
+ var meta=doc&&doc.querySelector?doc.querySelector(LAYOUT_GROUP_META):null;
1236
1368
  return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');
1237
1369
  }
1238
1370
  function checkLayoutChange(newLayout,targetUrl,newGroup){
1239
- var currentLayout=document.querySelector('meta[name="stx-layout"]');
1371
+ var currentLayout=qs(LAYOUT_META);
1240
1372
  var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';
1241
1373
  var curGroup=getCurrentLayoutGroup();
1242
1374
  var nextGroup=newGroup||defaultLayoutGroup(newLayout);
@@ -1265,9 +1397,14 @@ function getRouterScript() {
1265
1397
  // through swap() to each of the three history sites would have been the
1266
1398
  // alternative (#1807).
1267
1399
  function writeHistory(mode,href){
1268
- if(mode==='replace')history.replaceState({},'',href);
1269
- else history.pushState({},'',href);
1400
+ // The position belongs to the entry being left, and scrollToken still
1401
+ // names it here.
1402
+ rememberScroll();
1403
+ if(mode==='replace'){history.replaceState(tokenState(),'',href);return}
1404
+ scrollToken=newScrollToken();
1405
+ history.pushState(tokenState(),'',href);
1270
1406
  }
1407
+ function tokenState(){var state={};state[SCROLL_TOKEN]=scrollToken;return state}
1271
1408
 
1272
1409
  // Second arg accepts the legacy pushState boolean OR an options object
1273
1410
  // { replace }. Callers inside this file still pass the boolean.
@@ -1289,7 +1426,7 @@ function getRouterScript() {
1289
1426
 
1290
1427
  if(t.pathname===location.pathname&&t.hash){
1291
1428
  if(pushState!==false)writeHistory(pushState,t.href);
1292
- var el=document.querySelector(t.hash);
1429
+ var el=qs(t.hash);
1293
1430
  if(el)el.scrollIntoView({behavior:'smooth'});
1294
1431
  return Promise.resolve(true);
1295
1432
  }
@@ -1434,7 +1571,7 @@ else {
1434
1571
  function routeAnnouncer(){
1435
1572
  var el=document.getElementById('stx-route-announcer');
1436
1573
  if(el)return el;
1437
- el=document.createElement('div');
1574
+ el=ce('div');
1438
1575
  el.id='stx-route-announcer';
1439
1576
  el.setAttribute('aria-live','polite');
1440
1577
  el.setAttribute('role','status');
@@ -1467,7 +1604,7 @@ else {
1467
1604
  function focusAfterNavigation(hash){
1468
1605
  if(o.routeFocus===false)return;
1469
1606
  var target=null;
1470
- if(hash){try{target=document.querySelector(hash)}catch(e){target=null}}
1607
+ if(hash){try{target=qs(hash)}catch(e){target=null}}
1471
1608
  if(!target)target=getContainer();
1472
1609
  if(!target||!target.focus)return;
1473
1610
  // The container is not naturally focusable. tabindex="-1" makes it
@@ -1490,7 +1627,7 @@ else {
1490
1627
  // be the last one, every navigation, not just the first time it is seen.
1491
1628
  function findGeneratedCss(abs){
1492
1629
  var found=null;
1493
- document.querySelectorAll('head link[data-css][href]').forEach(function(link){
1630
+ qsa('head link[data-css][href]').forEach(function(link){
1494
1631
  if(new URL(link.getAttribute('href'),location.href).href!==abs)return;
1495
1632
  // Collapse duplicates, so moving one copy to the end can never leave an
1496
1633
  // older copy of the same sheet behind to fight it.
@@ -1519,7 +1656,7 @@ else {
1519
1656
  if(link.__stxCssReady)pending.push(link.__stxCssReady);
1520
1657
  return;
1521
1658
  }
1522
- link=document.createElement('link');
1659
+ link=ce('link');
1523
1660
  link.setAttribute('data-css','generated');
1524
1661
  link.setAttribute('rel','stylesheet');
1525
1662
  link.setAttribute('href',new URL(href,location.href).href===abs?href:abs);
@@ -1540,7 +1677,7 @@ else {
1540
1677
  link.onerror=function(){finish(true)};
1541
1678
  });
1542
1679
  pending.push(link.__stxCssReady);
1543
- document.head.appendChild(link);
1680
+ dhead().appendChild(link);
1544
1681
  });
1545
1682
  return pending.length?Promise.all(pending).then(function(results){return results.every(function(ok){return ok!==false})}):null;
1546
1683
  }
@@ -1576,7 +1713,7 @@ else {
1576
1713
  link.removeAttribute('data-stx-css-pending');
1577
1714
  link.removeAttribute('media');
1578
1715
  }
1579
- document.head.appendChild(link);
1716
+ dhead().appendChild(link);
1580
1717
  });
1581
1718
  }
1582
1719
 
@@ -1689,18 +1826,18 @@ else {
1689
1826
  });
1690
1827
  cleanFrag=cleanFrag.replace(new RegExp('<!--stx-held-script-(\\\\d+)-->','g'),function(m,i){return heldScripts[Number(i)]});
1691
1828
  // Remove old page styles (not css \u2014 that gets merged)
1692
- document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});
1829
+ qsa('style[data-stx-page]').forEach(function(s){s.remove()});
1693
1830
  // Merge css CSS from fragment into existing css style
1694
1831
  if(fragCss){
1695
- var curCss=document.querySelector('head style[data-css]');
1832
+ var curCss=qs('head style[data-css]');
1696
1833
  if(curCss){
1697
1834
  var merged=mergeCss(curCss.textContent||'',fragCss);
1698
1835
  if(merged)curCss.textContent=merged;
1699
1836
  }else{
1700
- var cw=document.createElement('style');
1837
+ var cw=ce('style');
1701
1838
  cw.setAttribute('data-css','generated');
1702
1839
  cw.textContent=fragCss;
1703
- document.head.appendChild(cw);
1840
+ dhead().appendChild(cw);
1704
1841
  }
1705
1842
  }
1706
1843
  // Already present ones are MOVED last, not skipped \u2014 see
@@ -1708,10 +1845,10 @@ else {
1708
1845
  promoteGeneratedCss(fragCssHrefs,location.href);
1709
1846
  // Add new page styles
1710
1847
  fragStyles.forEach(function(s){
1711
- var el=document.createElement('style');
1848
+ var el=ce('style');
1712
1849
  el.textContent=s.css;
1713
1850
  el.setAttribute('data-stx-page','');
1714
- document.head.appendChild(el);
1851
+ dhead().appendChild(el);
1715
1852
  });
1716
1853
  // Swap content \u2014 apply the destination container's own attributes first
1717
1854
  // so the incoming markup lands in a correctly-laid-out container instead
@@ -1719,11 +1856,10 @@ else {
1719
1856
  applyContainerAttrs(currentContent,pendingContainerAttrs);
1720
1857
  currentContent.innerHTML=cleanFrag;
1721
1858
  // Remove old page scripts
1722
- document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1859
+ qsa('script[data-stx-page]').forEach(function(s){s.remove()});
1723
1860
  if(pushState!==false)writeHistory(pushState,url+(hash||''));
1724
1861
  refreshCurrentLinks();
1725
- if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});
1726
- else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
1862
+ applyScroll(hash);
1727
1863
  window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
1728
1864
  // Before page scripts run, so a page that focuses its own control on
1729
1865
  // mount still wins \u2014 its script executes after this.
@@ -1733,7 +1869,7 @@ else {
1733
1869
  setTimeout(announceRoute,0);
1734
1870
  // Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
1735
1871
  log('[router] frag scripts:', fragScripts.length);
1736
- document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1872
+ qsa('script[data-stx-page]').forEach(function(s){s.remove()});
1737
1873
  // The page's module registry first (#1957), setups last. A fragment
1738
1874
  // lists its scripts in document order, and the registry is emitted
1739
1875
  // after the content it serves: a component that imports a package ran
@@ -1745,11 +1881,11 @@ else {
1745
1881
  fragScripts.forEach(function(entry){
1746
1882
  var code=entry.text;
1747
1883
  log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
1748
- var placeholder=document.querySelector('script[data-stx-route-script="'+entry.slot+'"]');
1884
+ var placeholder=qs('script[data-stx-route-script="'+entry.slot+'"]');
1749
1885
  var nestedScopedTemplate=false;
1750
1886
  var scopedLoopSetup=false;
1751
1887
  if(entry.scoped&&placeholder){
1752
- document.querySelectorAll('template').forEach(function(template){
1888
+ qsa('template').forEach(function(template){
1753
1889
  if(template.contains(placeholder)||(template.content&&template.content.contains(placeholder)))
1754
1890
  nestedScopedTemplate=true;
1755
1891
  });
@@ -1792,7 +1928,7 @@ else {
1792
1928
  return;
1793
1929
  }
1794
1930
  executedScriptHashes[h]=1;
1795
- var ns=document.createElement('script');
1931
+ var ns=ce('script');
1796
1932
  var hasImport=hasStaticImport(code);
1797
1933
  if(hasImport){
1798
1934
  ns.type='module';
@@ -1855,7 +1991,7 @@ else {
1855
1991
  var doc=parser.parseFromString(html,'text/html');
1856
1992
  // Full documents carry the build id as a meta rather than a header, and
1857
1993
  // this path also serves cached HTML, so check here too (#1772).
1858
- var docBuildMeta=doc.querySelector('meta[name="stx-build"]');
1994
+ var docBuildMeta=doc.querySelector(BUILD_META);
1859
1995
  var docBuild=docBuildMeta?(docBuildMeta.getAttribute('content')||''):'';
1860
1996
  if(isBuildSkew(docBuild)){reloadForSkew(url,docBuild);return Promise.resolve(false)}
1861
1997
  // Same hand-off as the fragment path, for the same reason (#1809, #1839).
@@ -1889,12 +2025,12 @@ else {
1889
2025
  // \u2500\u2500 Swap <head> styles \u2500\u2500
1890
2026
  // Inject new styles FIRST, then remove old to prevent unstyled flash
1891
2027
  var keepIds={'stx-view-transitions':1,'stx-r-css':1};
1892
- var curStyles=document.querySelectorAll('head style');
2028
+ var curStyles=qsa('head style');
1893
2029
  var newStyles=doc.querySelectorAll('head style');
1894
2030
 
1895
2031
  // Merge css styles instead of replacing \u2014 persistent elements
1896
2032
  // (nav, footer) outside <main> still need their utility classes
1897
- var curCss=document.querySelector('head style[data-css]');
2033
+ var curCss=qs('head style[data-css]');
1898
2034
  var newCss=null;
1899
2035
  newStyles.forEach(function(s){if(s.getAttribute('data-css'))newCss=s});
1900
2036
 
@@ -1906,20 +2042,20 @@ else {
1906
2042
  var incoming=[];
1907
2043
  newStyles.forEach(function(s){
1908
2044
  if(!keepIds[s.id]&&!s.getAttribute('data-css')){
1909
- var ns=document.createElement('style');
2045
+ var ns=ce('style');
1910
2046
  ns.textContent=s.textContent;
1911
2047
  ns.setAttribute('data-stx-incoming','');
1912
- document.head.appendChild(ns);
2048
+ dhead().appendChild(ns);
1913
2049
  incoming.push(ns);
1914
2050
  }
1915
2051
  });
1916
2052
 
1917
2053
  // If no existing css but new page has one, add it
1918
2054
  if(!curCss&&newCss){
1919
- var ns=document.createElement('style');
2055
+ var ns=ce('style');
1920
2056
  ns.textContent=newCss.textContent;
1921
2057
  ns.setAttribute('data-css',newCss.getAttribute('data-css'));
1922
- document.head.appendChild(ns);
2058
+ dhead().appendChild(ns);
1923
2059
  }
1924
2060
 
1925
2061
  // Remove old styles (except persistent ones, css, and incoming)
@@ -1946,7 +2082,7 @@ else {
1946
2082
  try{docBase=new URL(url,location.href).href}catch(e){docBase=location.href}
1947
2083
  var linkSel='link[rel="stylesheet"],link[rel="preconnect"]';
1948
2084
  var curLinks={};
1949
- document.querySelectorAll('head '+linkSel).forEach(function(l){var h=l.getAttribute('href');if(h)curLinks[new URL(h,location.href).href]=1});
2085
+ qsa('head '+linkSel).forEach(function(l){var h=l.getAttribute('href');if(h)curLinks[new URL(h,location.href).href]=1});
1950
2086
  doc.querySelectorAll('head '+linkSel).forEach(function(l){
1951
2087
  var href=l.getAttribute('href');
1952
2088
  if(!href)return;
@@ -1956,9 +2092,9 @@ else {
1956
2092
  var abs=new URL(href,docBase).href;
1957
2093
  if(curLinks[abs])return;
1958
2094
  curLinks[abs]=1;
1959
- var nl=document.createElement('link');
2095
+ var nl=ce('link');
1960
2096
  Array.from(l.attributes).forEach(function(a){nl.setAttribute(a.name,a.value)});
1961
- document.head.appendChild(nl);
2097
+ dhead().appendChild(nl);
1962
2098
  });
1963
2099
  // After every other stylesheet link, so the destination's complete
1964
2100
  // utility sheet is last in the cascade.
@@ -1972,8 +2108,8 @@ else {
1972
2108
  var declaredLayout=pendingLayoutDecl;
1973
2109
  pendingLayoutDecl=null;
1974
2110
  if(newBody){
1975
- var newMeta=doc.querySelector('meta[name="stx-layout"]');
1976
- var curMeta=document.querySelector('meta[name="stx-layout"]');
2111
+ var newMeta=doc.querySelector(LAYOUT_META);
2112
+ var curMeta=qs(LAYOUT_META);
1977
2113
  var curLayout=curMeta?curMeta.getAttribute('content'):'';
1978
2114
  // The server's answer wins over the document's metas. We are only here
1979
2115
  // because checkLayoutChange already said the group changed, based on
@@ -2041,16 +2177,16 @@ else {
2041
2177
  // Copy body attributes (class, data-stx, etc.)
2042
2178
  Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});
2043
2179
  // Update layout meta tag
2044
- var oldMeta=document.querySelector('meta[name="stx-layout"]');
2045
- var freshMeta=doc.querySelector('meta[name="stx-layout"]');
2180
+ var oldMeta=qs(LAYOUT_META);
2181
+ var freshMeta=doc.querySelector(LAYOUT_META);
2046
2182
  if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');
2047
- else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}
2048
- var oldGroupMeta=document.querySelector('meta[name="stx-layout-group"]');
2049
- var freshGroupMeta=doc.querySelector('meta[name="stx-layout-group"]');
2183
+ else if(freshMeta){var m=ce('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';dhead().appendChild(m)}
2184
+ var oldGroupMeta=qs(LAYOUT_GROUP_META);
2185
+ var freshGroupMeta=doc.querySelector(LAYOUT_GROUP_META);
2050
2186
  if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');
2051
- else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}
2187
+ else if(freshGroupMeta){var gm=ce('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';dhead().appendChild(gm)}
2052
2188
  // Update container reference for script execution below
2053
- currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;
2189
+ currentContent=qs(containerSel)||qs('main')||document.body;
2054
2190
  } else {
2055
2191
  // Same layout \u2014 swap only container content. Carry the destination
2056
2192
  // container's attributes across too (same reason as the fragment path:
@@ -2069,7 +2205,7 @@ else {
2069
2205
 
2070
2206
  // \u2500\u2500 Load new external <head> scripts \u2500\u2500
2071
2207
  var loadedSrcs={};
2072
- document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});
2208
+ qsa('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});
2073
2209
  var extPromises=[];
2074
2210
  doc.querySelectorAll('head script[src]').forEach(function(s){
2075
2211
  if(isSharedClientScript(s))return;
@@ -2079,11 +2215,11 @@ else {
2079
2215
  if(loadedSrcs[src])return;
2080
2216
  loadedSrcs[src]=1;
2081
2217
  extPromises.push(new Promise(function(resolve,reject){
2082
- var ns=document.createElement('script');
2218
+ var ns=ce('script');
2083
2219
  ns.src=src;
2084
2220
  ns.onload=resolve;
2085
2221
  ns.onerror=reject;
2086
- document.head.appendChild(ns);
2222
+ dhead().appendChild(ns);
2087
2223
  }));
2088
2224
  });
2089
2225
 
@@ -2097,7 +2233,7 @@ else {
2097
2233
 
2098
2234
  // \u2500\u2500 Script re-execution \u2500\u2500
2099
2235
  // Remove previously injected page scripts
2100
- document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
2236
+ qsa('script[data-stx-page]').forEach(function(s){s.remove()});
2101
2237
 
2102
2238
  var scripts=routedBodyScripts.slice();
2103
2239
  function addScript(text, runAlways, slot, owner){
@@ -2181,8 +2317,7 @@ else {
2181
2317
  refreshCurrentLinks();
2182
2318
 
2183
2319
  // Scroll
2184
- if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});
2185
- else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
2320
+ applyScroll(hash);
2186
2321
 
2187
2322
  // Update title
2188
2323
  var newTitle=doc.querySelector('title');
@@ -2243,7 +2378,7 @@ else {
2243
2378
  if(!runAlways&&!isSetup&&executedScriptHashes[h])return;
2244
2379
  executedScriptHashes[h]=1;
2245
2380
  // Wrap scripts with import statements as modules (Bug 3 fix)
2246
- var ns=document.createElement('script');
2381
+ var ns=ce('script');
2247
2382
  if(hasImport){
2248
2383
  ns.type='module';
2249
2384
  }
@@ -2256,7 +2391,7 @@ else {
2256
2391
  var alreadyScoped=runsAlways(typeof entry==='string'?'':entry.run,text);
2257
2392
  ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
2258
2393
  ns.setAttribute('data-stx-page','');
2259
- var placeholder=entry.slot?document.querySelector('script[data-stx-route-script="'+entry.slot+'"]'):null;
2394
+ var placeholder=entry.slot?qs('script[data-stx-route-script="'+entry.slot+'"]'):null;
2260
2395
  if(placeholder&&placeholder.parentNode){
2261
2396
  ns.setAttribute('data-stx-positioned','');
2262
2397
  placeholder.parentNode.replaceChild(ns,placeholder);
@@ -2548,6 +2683,16 @@ else {
2548
2683
 
2549
2684
  // \u2500\u2500 Back/forward \u2500\u2500
2550
2685
  window.addEventListener('popstate',function(){
2686
+ // Nothing has scrolled yet (restoration is manual), so the viewport still
2687
+ // shows the entry being left: save it under its own token before adopting
2688
+ // the popped entry's, and hand the swap the position to land on.
2689
+ rememberScroll();
2690
+ if(restoreScroll){
2691
+ var popped=history.state&&history.state[SCROLL_TOKEN];
2692
+ scrollToken=popped||newScrollToken();
2693
+ if(!popped)stampScrollToken();
2694
+ pendingScroll=readScroll(scrollToken);
2695
+ }
2551
2696
  navigate(location.pathname+location.search+location.hash,false);
2552
2697
  });
2553
2698
 
@@ -2614,7 +2759,7 @@ else {
2614
2759
  }
2615
2760
 
2616
2761
  function updateNav(){
2617
- document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){
2762
+ qsa('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){
2618
2763
  if(!a.hasAttribute('data-stx-link'))return;
2619
2764
  var st=linkState(a.getAttribute('href'),a.getAttribute('data-stx-active-match'));
2620
2765
  if(!st)return;
@@ -2635,7 +2780,7 @@ else {
2635
2780
 
2636
2781
  function updateActiveLinks(){
2637
2782
  // Update active classes on <stx-link> elements (and legacy data-stx-link)
2638
- var links=document.querySelectorAll('[data-stx-link]');
2783
+ var links=qsa('[data-stx-link]');
2639
2784
  links.forEach(function(link){
2640
2785
  var st=linkState(link.getAttribute('to')||link.getAttribute('href')||'',link.getAttribute('data-stx-active-match'));
2641
2786
  if(!st)return;
@@ -2677,7 +2822,7 @@ else {
2677
2822
  // the cascade, or opt out entirely with viewTransitions:false.
2678
2823
  function injectStyles(){
2679
2824
  if(!document.getElementById('stx-r-css')){
2680
- var s=document.createElement('style');s.id='stx-r-css';
2825
+ var s=ce('style');s.id='stx-r-css';
2681
2826
  // Strip characters that could escape our CSS block and inject new
2682
2827
  // declarations: ; { } ( ) " ' \\ < > plus whitespace control chars.
2683
2828
  // This keeps the value safe to concat into a stylesheet even if a
@@ -2699,10 +2844,10 @@ else {
2699
2844
  css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';
2700
2845
  }
2701
2846
  s.textContent=css;
2702
- document.head.appendChild(s);
2847
+ dhead().appendChild(s);
2703
2848
  }
2704
2849
  if(o.progress&&!document.getElementById('stx-router-progress')){
2705
- var el=document.createElement('div');
2850
+ var el=ce('div');
2706
2851
  el.id='stx-router-progress';
2707
2852
  el.setAttribute('role','progressbar');
2708
2853
  el.setAttribute('aria-hidden','true');
@@ -2719,7 +2864,7 @@ else {
2719
2864
 
2720
2865
  function injectViewTransitionCSS(){
2721
2866
  if(document.getElementById('stx-view-transitions'))return;
2722
- var s=document.createElement('style');s.id='stx-view-transitions';
2867
+ var s=ce('style');s.id='stx-view-transitions';
2723
2868
  s.textContent='::view-transition-group(root){animation:none}::view-transition-old(root){animation:none}::view-transition-new(root){animation:none}main,#app-content,[data-stx-content]{view-transition-name:stx-content}::view-transition-old(stx-content){animation:stx-fade-out .15s ease-out both}::view-transition-new(stx-content){animation:stx-fade-in .15s ease-in .1s both}@keyframes stx-fade-out{from{opacity:1}to{opacity:0}}@keyframes stx-fade-in{from{opacity:0}to{opacity:1}}::view-transition{background:transparent}::view-transition-group(stx-content){background:inherit;overflow:hidden}';
2724
2869
  (document.head||document.documentElement).appendChild(s);
2725
2870
  }
@@ -2777,9 +2922,19 @@ else {
2777
2922
  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
2778
2923
  })();
2779
2924
  `;
2780
- cachedRouterScript = minifyRouterScript(source);
2925
+ }
2926
+ function getRouterScript() {
2927
+ if (cachedRouterScript)
2928
+ return cachedRouterScript;
2929
+ cachedRouterScript = minifyRouterScript(stripRouterLogs(routerSource()));
2781
2930
  return cachedRouterScript;
2782
2931
  }
2932
+ function getRouterScriptDev() {
2933
+ if (cachedRouterScriptDev)
2934
+ return cachedRouterScriptDev;
2935
+ cachedRouterScriptDev = minifyRouterScript(routerSource());
2936
+ return cachedRouterScriptDev;
2937
+ }
2783
2938
  export {
2784
2939
  setAppUrl,
2785
2940
  runMiddleware,
@@ -2794,6 +2949,7 @@ export {
2794
2949
  isNonPageRoutePattern,
2795
2950
  isNonPageRoutePath,
2796
2951
  hasMiddleware,
2952
+ getRouterScriptDev,
2797
2953
  getRouterScript,
2798
2954
  getMiddlewareNames,
2799
2955
  getMiddleware,
package/dist/serve.js CHANGED
@@ -9791,7 +9791,7 @@ function __stxOverlay(errs){
9791
9791
  rebuildDiscoveredPagePaths(kept);
9792
9792
  await indexPageMiddleware(kept);
9793
9793
  try {
9794
- const { Router } = await import("./chunk-pg39gv16.js");
9794
+ const { Router } = await import("./chunk-zd4nfpfq.js");
9795
9795
  const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
9796
9796
  const router = new Router(process19.cwd(), { pagesDirs, stateDir: stateDirName(), exclude: options.exclude });
9797
9797
  if (!options.quiet)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-plugin-stx",
3
3
  "type": "module",
4
- "version": "0.2.337",
4
+ "version": "0.2.339",
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.337",
78
- "stx-router": "0.2.337"
77
+ "@stacksjs/stx": "0.2.339",
78
+ "stx-router": "0.2.339"
79
79
  }
80
80
  }