stx-router 0.2.337 → 0.2.338

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.d.ts CHANGED
@@ -1 +1,16 @@
1
+ /**
2
+ * The router script as it ships: debug logging removed, then minified.
3
+ *
4
+ * Every page carries this, so the 32 `log()` call sites -- 2.3KB of the
5
+ * delivered bytes, printing nothing unless `debug` is set -- are not part of
6
+ * it. Stripping happens outside the try, as in generateSignalsRuntime: losing
7
+ * minification makes the script bigger, losing the strip would ship the logs.
8
+ */
1
9
  export declare function getRouterScript(): string;
10
+ /**
11
+ * The router script with its `log()` calls intact, for development.
12
+ *
13
+ * Set `debug` in the router config to see them. Minified the same way, so what
14
+ * a developer runs behaves exactly like what ships.
15
+ */
16
+ export declare function getRouterScriptDev(): string;
package/dist/client.js CHANGED
@@ -1,4 +1,45 @@
1
- let cachedRouterScript;
1
+ let cachedRouterScript, cachedRouterScriptDev;
2
+ function stripRouterLogs(source) {
3
+ const out = [], needle = "log(";
4
+ let i = 0;
5
+ while (i < source.length) {
6
+ const hit = source.indexOf(needle, i);
7
+ if (hit === -1) {
8
+ out.push(source.slice(i));
9
+ break;
10
+ }
11
+ const before = hit > 0 ? source[hit - 1] : " ", isDeclaration = /\bfunction\s+$/.test(source.slice(Math.max(0, hit - 16), hit));
12
+ if (/[\w$.]/.test(before) || isDeclaration) {
13
+ out.push(source.slice(i, hit + needle.length));
14
+ i = hit + needle.length;
15
+ continue;
16
+ }
17
+ out.push(source.slice(i, hit));
18
+ let depth = 1, j = hit + needle.length;
19
+ while (j < source.length && depth > 0) {
20
+ const character = source[j];
21
+ if (character === '"' || character === "'" || character === "`") {
22
+ const quote = character;
23
+ j++;
24
+ while (j < source.length && source[j] !== quote) {
25
+ if (source[j] === "\\")
26
+ j++;
27
+ j++;
28
+ }
29
+ j++;
30
+ continue;
31
+ }
32
+ if (character === "(")
33
+ depth++;
34
+ else if (character === ")")
35
+ depth--;
36
+ j++;
37
+ }
38
+ out.push("0");
39
+ i = j;
40
+ }
41
+ return out.join("");
42
+ }
2
43
  function minifyRouterScript(source) {
3
44
  try {
4
45
  return new Bun.Transpiler({ loader: "js", minifyWhitespace: !0 }).transformSync(source).replace(/\}(var |let |const |function )/g, "};$1").trim();
@@ -6,10 +47,8 @@ function minifyRouterScript(source) {
6
47
  return source;
7
48
  }
8
49
  }
9
- export function getRouterScript() {
10
- if (cachedRouterScript)
11
- return cachedRouterScript;
12
- cachedRouterScript = minifyRouterScript(`
50
+ function routerSource() {
51
+ return `
13
52
  ;(function(){
14
53
  'use strict';
15
54
  var ROUTER_REV=7;
@@ -22,6 +61,17 @@ export function getRouterScript() {
22
61
  var debug=!!o.debug;
23
62
  function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}
24
63
 
64
+ // Shorthands for the document-rooted queries this script makes ~33 times.
65
+ // Every page carries these bytes, and the budget in
66
+ // scripts/performance-budgets.ts is what noticed.
67
+ var LAYOUT_META='meta[name="stx-layout"]';
68
+ var LAYOUT_GROUP_META='meta[name="stx-layout-group"]';
69
+ var BUILD_META='meta[name="stx-build"]';
70
+ function qs(sel){return document.querySelector(sel)}
71
+ function qsa(sel){return document.querySelectorAll(sel)}
72
+ function ce(tag){return document.createElement(tag)}
73
+ function dhead(){return document.head}
74
+
25
75
  // Replace, never merge: absent payloads must forget the outgoing page's data.
26
76
  // Run at the committed swap, not during prefetch or a superseded navigation.
27
77
  function hydrateServerData(html){
@@ -44,7 +94,7 @@ export function getRouterScript() {
44
94
  // Conservative on purpose: act only when BOTH ids are known. A missing id is
45
95
  // "no information", never "mismatch", so statically hosted output (no headers)
46
96
  // and older servers behave exactly as before.
47
- var buildMeta=document.querySelector('meta[name="stx-build"]');
97
+ var buildMeta=qs(BUILD_META);
48
98
  var loadedBuild=buildMeta?(buildMeta.getAttribute('content')||''):'';
49
99
  window.__stxBuild=loadedBuild;
50
100
  function isBuildSkew(incoming){
@@ -192,7 +242,7 @@ export function getRouterScript() {
192
242
  function parseContainerAttrs(attrStr){
193
243
  var map={};
194
244
  if(!attrStr)return map;
195
- var probe=document.createElement('div');
245
+ var probe=ce('div');
196
246
  probe.innerHTML='<i '+attrStr+'></i>';
197
247
  var src=probe.firstChild;
198
248
  // getAttributeNames first: it is the same standard DOM answer and it is
@@ -281,7 +331,7 @@ export function getRouterScript() {
281
331
  }
282
332
 
283
333
  function getContainer(){
284
- return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');
334
+ return qs(containerSel)||qs('[data-stx-content]')||qs('main');
285
335
  }
286
336
 
287
337
  // \u2500\u2500 Navigation \u2500\u2500
@@ -365,7 +415,7 @@ export function getRouterScript() {
365
415
  // navigation. Scripts without an owner are unaffected.
366
416
  function ownerStays(owner){
367
417
  if(!owner)return false;
368
- var root=document.querySelector('[data-stx-scope="'+owner+'"]');
418
+ var root=qs('[data-stx-scope="'+owner+'"]');
369
419
  return !root||typeof root.__stx_disposers==='function';
370
420
  }
371
421
  function hashScript(code){
@@ -532,7 +582,7 @@ export function getRouterScript() {
532
582
  if(!key||loadedExternalScripts[key])return null;
533
583
  loadedExternalScripts[key]=1;
534
584
  return new Promise(function(resolve){
535
- var el=document.createElement('script');
585
+ var el=ce('script');
536
586
  el.src=key;
537
587
  // Deliberately NOT data-stx-page: both swap paths clear those on every
538
588
  // navigation, which would sweep this away and \u2014 since it is never loaded
@@ -541,12 +591,12 @@ export function getRouterScript() {
541
591
  el.setAttribute('data-stx-external','');
542
592
  el.onload=function(){resolve()};
543
593
  el.onerror=function(){log('[router] container script failed:',key);resolve()};
544
- document.head.appendChild(el);
594
+ dhead().appendChild(el);
545
595
  });
546
596
  }
547
597
 
548
598
  // Record scripts from the initial page load
549
- document.querySelectorAll('script').forEach(function(s){
599
+ qsa('script').forEach(function(s){
550
600
  var text=s.textContent||'';
551
601
  if(s.hasAttribute('src'))rememberExternalScript(s.getAttribute('src'));
552
602
  else if(text.trim())executedScriptHashes[hashScript(text)]=1;
@@ -573,17 +623,17 @@ export function getRouterScript() {
573
623
  return part?part.replace(/\\.stx$/i,''):'app';
574
624
  }
575
625
  function getCurrentLayoutGroup(){
576
- var meta=document.querySelector('meta[name="stx-layout-group"]');
626
+ var meta=qs(LAYOUT_GROUP_META);
577
627
  if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');
578
- var layout=document.querySelector('meta[name="stx-layout"]');
628
+ var layout=qs(LAYOUT_META);
579
629
  return defaultLayoutGroup(layout?layout.getAttribute('content'):'');
580
630
  }
581
631
  function getDocLayoutGroup(doc,layout){
582
- var meta=doc&&doc.querySelector?doc.querySelector('meta[name="stx-layout-group"]'):null;
632
+ var meta=doc&&doc.querySelector?doc.querySelector(LAYOUT_GROUP_META):null;
583
633
  return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');
584
634
  }
585
635
  function checkLayoutChange(newLayout,targetUrl,newGroup){
586
- var currentLayout=document.querySelector('meta[name="stx-layout"]');
636
+ var currentLayout=qs(LAYOUT_META);
587
637
  var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';
588
638
  var curGroup=getCurrentLayoutGroup();
589
639
  var nextGroup=newGroup||defaultLayoutGroup(newLayout);
@@ -636,7 +686,7 @@ export function getRouterScript() {
636
686
 
637
687
  if(t.pathname===location.pathname&&t.hash){
638
688
  if(pushState!==false)writeHistory(pushState,t.href);
639
- var el=document.querySelector(t.hash);
689
+ var el=qs(t.hash);
640
690
  if(el)el.scrollIntoView({behavior:'smooth'});
641
691
  return Promise.resolve(true);
642
692
  }
@@ -781,7 +831,7 @@ else {
781
831
  function routeAnnouncer(){
782
832
  var el=document.getElementById('stx-route-announcer');
783
833
  if(el)return el;
784
- el=document.createElement('div');
834
+ el=ce('div');
785
835
  el.id='stx-route-announcer';
786
836
  el.setAttribute('aria-live','polite');
787
837
  el.setAttribute('role','status');
@@ -814,7 +864,7 @@ else {
814
864
  function focusAfterNavigation(hash){
815
865
  if(o.routeFocus===false)return;
816
866
  var target=null;
817
- if(hash){try{target=document.querySelector(hash)}catch(e){target=null}}
867
+ if(hash){try{target=qs(hash)}catch(e){target=null}}
818
868
  if(!target)target=getContainer();
819
869
  if(!target||!target.focus)return;
820
870
  // The container is not naturally focusable. tabindex="-1" makes it
@@ -837,7 +887,7 @@ else {
837
887
  // be the last one, every navigation, not just the first time it is seen.
838
888
  function findGeneratedCss(abs){
839
889
  var found=null;
840
- document.querySelectorAll('head link[data-css][href]').forEach(function(link){
890
+ qsa('head link[data-css][href]').forEach(function(link){
841
891
  if(new URL(link.getAttribute('href'),location.href).href!==abs)return;
842
892
  // Collapse duplicates, so moving one copy to the end can never leave an
843
893
  // older copy of the same sheet behind to fight it.
@@ -866,7 +916,7 @@ else {
866
916
  if(link.__stxCssReady)pending.push(link.__stxCssReady);
867
917
  return;
868
918
  }
869
- link=document.createElement('link');
919
+ link=ce('link');
870
920
  link.setAttribute('data-css','generated');
871
921
  link.setAttribute('rel','stylesheet');
872
922
  link.setAttribute('href',new URL(href,location.href).href===abs?href:abs);
@@ -887,7 +937,7 @@ else {
887
937
  link.onerror=function(){finish(true)};
888
938
  });
889
939
  pending.push(link.__stxCssReady);
890
- document.head.appendChild(link);
940
+ dhead().appendChild(link);
891
941
  });
892
942
  return pending.length?Promise.all(pending).then(function(results){return results.every(function(ok){return ok!==false})}):null;
893
943
  }
@@ -923,7 +973,7 @@ else {
923
973
  link.removeAttribute('data-stx-css-pending');
924
974
  link.removeAttribute('media');
925
975
  }
926
- document.head.appendChild(link);
976
+ dhead().appendChild(link);
927
977
  });
928
978
  }
929
979
 
@@ -1036,18 +1086,18 @@ else {
1036
1086
  });
1037
1087
  cleanFrag=cleanFrag.replace(new RegExp('<!--stx-held-script-(\\\\d+)-->','g'),function(m,i){return heldScripts[Number(i)]});
1038
1088
  // Remove old page styles (not css \u2014 that gets merged)
1039
- document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});
1089
+ qsa('style[data-stx-page]').forEach(function(s){s.remove()});
1040
1090
  // Merge css CSS from fragment into existing css style
1041
1091
  if(fragCss){
1042
- var curCss=document.querySelector('head style[data-css]');
1092
+ var curCss=qs('head style[data-css]');
1043
1093
  if(curCss){
1044
1094
  var merged=mergeCss(curCss.textContent||'',fragCss);
1045
1095
  if(merged)curCss.textContent=merged;
1046
1096
  }else{
1047
- var cw=document.createElement('style');
1097
+ var cw=ce('style');
1048
1098
  cw.setAttribute('data-css','generated');
1049
1099
  cw.textContent=fragCss;
1050
- document.head.appendChild(cw);
1100
+ dhead().appendChild(cw);
1051
1101
  }
1052
1102
  }
1053
1103
  // Already present ones are MOVED last, not skipped \u2014 see
@@ -1055,10 +1105,10 @@ else {
1055
1105
  promoteGeneratedCss(fragCssHrefs,location.href);
1056
1106
  // Add new page styles
1057
1107
  fragStyles.forEach(function(s){
1058
- var el=document.createElement('style');
1108
+ var el=ce('style');
1059
1109
  el.textContent=s.css;
1060
1110
  el.setAttribute('data-stx-page','');
1061
- document.head.appendChild(el);
1111
+ dhead().appendChild(el);
1062
1112
  });
1063
1113
  // Swap content \u2014 apply the destination container's own attributes first
1064
1114
  // so the incoming markup lands in a correctly-laid-out container instead
@@ -1066,11 +1116,11 @@ else {
1066
1116
  applyContainerAttrs(currentContent,pendingContainerAttrs);
1067
1117
  currentContent.innerHTML=cleanFrag;
1068
1118
  // Remove old page scripts
1069
- document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1119
+ qsa('script[data-stx-page]').forEach(function(s){s.remove()});
1070
1120
  if(pushState!==false)writeHistory(pushState,url+(hash||''));
1071
1121
  refreshCurrentLinks();
1072
1122
  if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});
1073
- else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
1123
+ else if(hash){var el=qs(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
1074
1124
  window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
1075
1125
  // Before page scripts run, so a page that focuses its own control on
1076
1126
  // mount still wins \u2014 its script executes after this.
@@ -1080,7 +1130,7 @@ else {
1080
1130
  setTimeout(announceRoute,0);
1081
1131
  // Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
1082
1132
  log('[router] frag scripts:', fragScripts.length);
1083
- document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1133
+ qsa('script[data-stx-page]').forEach(function(s){s.remove()});
1084
1134
  // The page's module registry first (#1957), setups last. A fragment
1085
1135
  // lists its scripts in document order, and the registry is emitted
1086
1136
  // after the content it serves: a component that imports a package ran
@@ -1092,11 +1142,11 @@ else {
1092
1142
  fragScripts.forEach(function(entry){
1093
1143
  var code=entry.text;
1094
1144
  log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
1095
- var placeholder=document.querySelector('script[data-stx-route-script="'+entry.slot+'"]');
1145
+ var placeholder=qs('script[data-stx-route-script="'+entry.slot+'"]');
1096
1146
  var nestedScopedTemplate=false;
1097
1147
  var scopedLoopSetup=false;
1098
1148
  if(entry.scoped&&placeholder){
1099
- document.querySelectorAll('template').forEach(function(template){
1149
+ qsa('template').forEach(function(template){
1100
1150
  if(template.contains(placeholder)||(template.content&&template.content.contains(placeholder)))
1101
1151
  nestedScopedTemplate=true;
1102
1152
  });
@@ -1139,7 +1189,7 @@ else {
1139
1189
  return;
1140
1190
  }
1141
1191
  executedScriptHashes[h]=1;
1142
- var ns=document.createElement('script');
1192
+ var ns=ce('script');
1143
1193
  var hasImport=hasStaticImport(code);
1144
1194
  if(hasImport){
1145
1195
  ns.type='module';
@@ -1202,7 +1252,7 @@ else {
1202
1252
  var doc=parser.parseFromString(html,'text/html');
1203
1253
  // Full documents carry the build id as a meta rather than a header, and
1204
1254
  // this path also serves cached HTML, so check here too (#1772).
1205
- var docBuildMeta=doc.querySelector('meta[name="stx-build"]');
1255
+ var docBuildMeta=doc.querySelector(BUILD_META);
1206
1256
  var docBuild=docBuildMeta?(docBuildMeta.getAttribute('content')||''):'';
1207
1257
  if(isBuildSkew(docBuild)){reloadForSkew(url,docBuild);return Promise.resolve(false)}
1208
1258
  // Same hand-off as the fragment path, for the same reason (#1809, #1839).
@@ -1236,12 +1286,12 @@ else {
1236
1286
  // \u2500\u2500 Swap <head> styles \u2500\u2500
1237
1287
  // Inject new styles FIRST, then remove old to prevent unstyled flash
1238
1288
  var keepIds={'stx-view-transitions':1,'stx-r-css':1};
1239
- var curStyles=document.querySelectorAll('head style');
1289
+ var curStyles=qsa('head style');
1240
1290
  var newStyles=doc.querySelectorAll('head style');
1241
1291
 
1242
1292
  // Merge css styles instead of replacing \u2014 persistent elements
1243
1293
  // (nav, footer) outside <main> still need their utility classes
1244
- var curCss=document.querySelector('head style[data-css]');
1294
+ var curCss=qs('head style[data-css]');
1245
1295
  var newCss=null;
1246
1296
  newStyles.forEach(function(s){if(s.getAttribute('data-css'))newCss=s});
1247
1297
 
@@ -1253,20 +1303,20 @@ else {
1253
1303
  var incoming=[];
1254
1304
  newStyles.forEach(function(s){
1255
1305
  if(!keepIds[s.id]&&!s.getAttribute('data-css')){
1256
- var ns=document.createElement('style');
1306
+ var ns=ce('style');
1257
1307
  ns.textContent=s.textContent;
1258
1308
  ns.setAttribute('data-stx-incoming','');
1259
- document.head.appendChild(ns);
1309
+ dhead().appendChild(ns);
1260
1310
  incoming.push(ns);
1261
1311
  }
1262
1312
  });
1263
1313
 
1264
1314
  // If no existing css but new page has one, add it
1265
1315
  if(!curCss&&newCss){
1266
- var ns=document.createElement('style');
1316
+ var ns=ce('style');
1267
1317
  ns.textContent=newCss.textContent;
1268
1318
  ns.setAttribute('data-css',newCss.getAttribute('data-css'));
1269
- document.head.appendChild(ns);
1319
+ dhead().appendChild(ns);
1270
1320
  }
1271
1321
 
1272
1322
  // Remove old styles (except persistent ones, css, and incoming)
@@ -1293,7 +1343,7 @@ else {
1293
1343
  try{docBase=new URL(url,location.href).href}catch(e){docBase=location.href}
1294
1344
  var linkSel='link[rel="stylesheet"],link[rel="preconnect"]';
1295
1345
  var curLinks={};
1296
- document.querySelectorAll('head '+linkSel).forEach(function(l){var h=l.getAttribute('href');if(h)curLinks[new URL(h,location.href).href]=1});
1346
+ qsa('head '+linkSel).forEach(function(l){var h=l.getAttribute('href');if(h)curLinks[new URL(h,location.href).href]=1});
1297
1347
  doc.querySelectorAll('head '+linkSel).forEach(function(l){
1298
1348
  var href=l.getAttribute('href');
1299
1349
  if(!href)return;
@@ -1303,9 +1353,9 @@ else {
1303
1353
  var abs=new URL(href,docBase).href;
1304
1354
  if(curLinks[abs])return;
1305
1355
  curLinks[abs]=1;
1306
- var nl=document.createElement('link');
1356
+ var nl=ce('link');
1307
1357
  Array.from(l.attributes).forEach(function(a){nl.setAttribute(a.name,a.value)});
1308
- document.head.appendChild(nl);
1358
+ dhead().appendChild(nl);
1309
1359
  });
1310
1360
  // After every other stylesheet link, so the destination's complete
1311
1361
  // utility sheet is last in the cascade.
@@ -1319,8 +1369,8 @@ else {
1319
1369
  var declaredLayout=pendingLayoutDecl;
1320
1370
  pendingLayoutDecl=null;
1321
1371
  if(newBody){
1322
- var newMeta=doc.querySelector('meta[name="stx-layout"]');
1323
- var curMeta=document.querySelector('meta[name="stx-layout"]');
1372
+ var newMeta=doc.querySelector(LAYOUT_META);
1373
+ var curMeta=qs(LAYOUT_META);
1324
1374
  var curLayout=curMeta?curMeta.getAttribute('content'):'';
1325
1375
  // The server's answer wins over the document's metas. We are only here
1326
1376
  // because checkLayoutChange already said the group changed, based on
@@ -1388,16 +1438,16 @@ else {
1388
1438
  // Copy body attributes (class, data-stx, etc.)
1389
1439
  Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});
1390
1440
  // Update layout meta tag
1391
- var oldMeta=document.querySelector('meta[name="stx-layout"]');
1392
- var freshMeta=doc.querySelector('meta[name="stx-layout"]');
1441
+ var oldMeta=qs(LAYOUT_META);
1442
+ var freshMeta=doc.querySelector(LAYOUT_META);
1393
1443
  if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');
1394
- else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}
1395
- var oldGroupMeta=document.querySelector('meta[name="stx-layout-group"]');
1396
- var freshGroupMeta=doc.querySelector('meta[name="stx-layout-group"]');
1444
+ else if(freshMeta){var m=ce('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';dhead().appendChild(m)}
1445
+ var oldGroupMeta=qs(LAYOUT_GROUP_META);
1446
+ var freshGroupMeta=doc.querySelector(LAYOUT_GROUP_META);
1397
1447
  if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');
1398
- else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}
1448
+ else if(freshGroupMeta){var gm=ce('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';dhead().appendChild(gm)}
1399
1449
  // Update container reference for script execution below
1400
- currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;
1450
+ currentContent=qs(containerSel)||qs('main')||document.body;
1401
1451
  } else {
1402
1452
  // Same layout \u2014 swap only container content. Carry the destination
1403
1453
  // container's attributes across too (same reason as the fragment path:
@@ -1416,7 +1466,7 @@ else {
1416
1466
 
1417
1467
  // \u2500\u2500 Load new external <head> scripts \u2500\u2500
1418
1468
  var loadedSrcs={};
1419
- document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});
1469
+ qsa('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});
1420
1470
  var extPromises=[];
1421
1471
  doc.querySelectorAll('head script[src]').forEach(function(s){
1422
1472
  if(isSharedClientScript(s))return;
@@ -1426,11 +1476,11 @@ else {
1426
1476
  if(loadedSrcs[src])return;
1427
1477
  loadedSrcs[src]=1;
1428
1478
  extPromises.push(new Promise(function(resolve,reject){
1429
- var ns=document.createElement('script');
1479
+ var ns=ce('script');
1430
1480
  ns.src=src;
1431
1481
  ns.onload=resolve;
1432
1482
  ns.onerror=reject;
1433
- document.head.appendChild(ns);
1483
+ dhead().appendChild(ns);
1434
1484
  }));
1435
1485
  });
1436
1486
 
@@ -1444,7 +1494,7 @@ else {
1444
1494
 
1445
1495
  // \u2500\u2500 Script re-execution \u2500\u2500
1446
1496
  // Remove previously injected page scripts
1447
- document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1497
+ qsa('script[data-stx-page]').forEach(function(s){s.remove()});
1448
1498
 
1449
1499
  var scripts=routedBodyScripts.slice();
1450
1500
  function addScript(text, runAlways, slot, owner){
@@ -1529,7 +1579,7 @@ else {
1529
1579
 
1530
1580
  // Scroll
1531
1581
  if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});
1532
- else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
1582
+ else if(hash){var el=qs(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
1533
1583
 
1534
1584
  // Update title
1535
1585
  var newTitle=doc.querySelector('title');
@@ -1590,7 +1640,7 @@ else {
1590
1640
  if(!runAlways&&!isSetup&&executedScriptHashes[h])return;
1591
1641
  executedScriptHashes[h]=1;
1592
1642
  // Wrap scripts with import statements as modules (Bug 3 fix)
1593
- var ns=document.createElement('script');
1643
+ var ns=ce('script');
1594
1644
  if(hasImport){
1595
1645
  ns.type='module';
1596
1646
  }
@@ -1603,7 +1653,7 @@ else {
1603
1653
  var alreadyScoped=runsAlways(typeof entry==='string'?'':entry.run,text);
1604
1654
  ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
1605
1655
  ns.setAttribute('data-stx-page','');
1606
- var placeholder=entry.slot?document.querySelector('script[data-stx-route-script="'+entry.slot+'"]'):null;
1656
+ var placeholder=entry.slot?qs('script[data-stx-route-script="'+entry.slot+'"]'):null;
1607
1657
  if(placeholder&&placeholder.parentNode){
1608
1658
  ns.setAttribute('data-stx-positioned','');
1609
1659
  placeholder.parentNode.replaceChild(ns,placeholder);
@@ -1961,7 +2011,7 @@ else {
1961
2011
  }
1962
2012
 
1963
2013
  function updateNav(){
1964
- document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){
2014
+ qsa('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){
1965
2015
  if(!a.hasAttribute('data-stx-link'))return;
1966
2016
  var st=linkState(a.getAttribute('href'),a.getAttribute('data-stx-active-match'));
1967
2017
  if(!st)return;
@@ -1982,7 +2032,7 @@ else {
1982
2032
 
1983
2033
  function updateActiveLinks(){
1984
2034
  // Update active classes on <stx-link> elements (and legacy data-stx-link)
1985
- var links=document.querySelectorAll('[data-stx-link]');
2035
+ var links=qsa('[data-stx-link]');
1986
2036
  links.forEach(function(link){
1987
2037
  var st=linkState(link.getAttribute('to')||link.getAttribute('href')||'',link.getAttribute('data-stx-active-match'));
1988
2038
  if(!st)return;
@@ -2024,7 +2074,7 @@ else {
2024
2074
  // the cascade, or opt out entirely with viewTransitions:false.
2025
2075
  function injectStyles(){
2026
2076
  if(!document.getElementById('stx-r-css')){
2027
- var s=document.createElement('style');s.id='stx-r-css';
2077
+ var s=ce('style');s.id='stx-r-css';
2028
2078
  // Strip characters that could escape our CSS block and inject new
2029
2079
  // declarations: ; { } ( ) " ' \\ < > plus whitespace control chars.
2030
2080
  // This keeps the value safe to concat into a stylesheet even if a
@@ -2046,10 +2096,10 @@ else {
2046
2096
  css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';
2047
2097
  }
2048
2098
  s.textContent=css;
2049
- document.head.appendChild(s);
2099
+ dhead().appendChild(s);
2050
2100
  }
2051
2101
  if(o.progress&&!document.getElementById('stx-router-progress')){
2052
- var el=document.createElement('div');
2102
+ var el=ce('div');
2053
2103
  el.id='stx-router-progress';
2054
2104
  el.setAttribute('role','progressbar');
2055
2105
  el.setAttribute('aria-hidden','true');
@@ -2066,7 +2116,7 @@ else {
2066
2116
 
2067
2117
  function injectViewTransitionCSS(){
2068
2118
  if(document.getElementById('stx-view-transitions'))return;
2069
- var s=document.createElement('style');s.id='stx-view-transitions';
2119
+ var s=ce('style');s.id='stx-view-transitions';
2070
2120
  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}';
2071
2121
  (document.head||document.documentElement).appendChild(s);
2072
2122
  }
@@ -2123,6 +2173,17 @@ else {
2123
2173
 
2124
2174
  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
2125
2175
  })();
2126
- `);
2176
+ `;
2177
+ }
2178
+ export function getRouterScript() {
2179
+ if (cachedRouterScript)
2180
+ return cachedRouterScript;
2181
+ cachedRouterScript = minifyRouterScript(stripRouterLogs(routerSource()));
2127
2182
  return cachedRouterScript;
2128
2183
  }
2184
+ export function getRouterScriptDev() {
2185
+ if (cachedRouterScriptDev)
2186
+ return cachedRouterScriptDev;
2187
+ cachedRouterScriptDev = minifyRouterScript(routerSource());
2188
+ return cachedRouterScriptDev;
2189
+ }
package/dist/index.d.ts CHANGED
@@ -52,4 +52,4 @@ export { generateRouteTypes, generateRouteManifest } from './codegen';
52
52
  // Which files under a page root are public routes (#1866)
53
53
  export { NON_PAGE_DIRS, isNonPageRoutePath, isNonPageRoutePattern } from './page-routes';
54
54
  // Client-side SPA navigation
55
- export { getRouterScript } from './client';
55
+ export { getRouterScript, getRouterScriptDev } from './client';
package/dist/index.js CHANGED
@@ -22,4 +22,4 @@ export { resolveLayoutChain } from "./nested-layouts.js";
22
22
  export { deriveLayoutGroup, extractLayoutMetadata } from "./layout-metadata.js";
23
23
  export { generateRouteTypes, generateRouteManifest } from "./codegen.js";
24
24
  export { NON_PAGE_DIRS, isNonPageRoutePath, isNonPageRoutePattern } from "./page-routes.js";
25
- export { getRouterScript } from "./client.js";
25
+ export { getRouterScript, getRouterScriptDev } from "./client.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stx-router",
3
3
  "type": "module",
4
- "version": "0.2.337",
4
+ "version": "0.2.338",
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",