stx-router 0.2.113 → 0.2.115
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 +83 -35
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -16,6 +16,7 @@ export function getRouterScript() {
|
|
|
16
16
|
try{
|
|
17
17
|
var transition=document.startViewTransition(callback);
|
|
18
18
|
var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};
|
|
19
|
+
if(transition&&transition.ready&&transition.ready.catch)transition.ready.catch(onAbort);
|
|
19
20
|
if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);
|
|
20
21
|
if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);
|
|
21
22
|
return true;
|
|
@@ -209,6 +210,18 @@ export function getRouterScript() {
|
|
|
209
210
|
var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}
|
|
210
211
|
return h;
|
|
211
212
|
}
|
|
213
|
+
function hasStaticImport(code){
|
|
214
|
+
return /(?:^|\\n)[ \\t]*import(?:[ \\t]+|[{*'"])/.test(code);
|
|
215
|
+
}
|
|
216
|
+
function generatedSetupName(code){
|
|
217
|
+
var match=code.match(/function (__stx_setup_[A-Za-z0-9_]+)\\s*\\(/);
|
|
218
|
+
return match?match[1]:'';
|
|
219
|
+
}
|
|
220
|
+
function isSignalsRuntimeScript(script,code){
|
|
221
|
+
return !!(script&&script.hasAttribute&&script.hasAttribute('data-stx-runtime'))
|
|
222
|
+
||(code.indexOf('_cleanupContainer')!==-1&&code.indexOf('signals runtime loading')!==-1)
|
|
223
|
+
||code.indexOf("'use strict';var cloakStyle")!==-1;
|
|
224
|
+
}
|
|
212
225
|
// Record scripts from the initial page load
|
|
213
226
|
document.querySelectorAll('script').forEach(function(s){
|
|
214
227
|
var text=s.textContent||'';
|
|
@@ -400,10 +413,15 @@ else {
|
|
|
400
413
|
function doFragSwap(){
|
|
401
414
|
// Extract scripts from fragment before injecting HTML
|
|
402
415
|
var fragScripts=[];
|
|
416
|
+
var fragScriptId=0;
|
|
403
417
|
var fragStyles=[];
|
|
404
418
|
var fragCrosswindCSS=null;
|
|
405
|
-
var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\b[^>]
|
|
406
|
-
if(code&&code.trim())
|
|
419
|
+
var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\b([^>]*)>([\\\\s\\\\S]*?)<\\\\/scr'+'ipt>','gi'),function(m,attrs,code){
|
|
420
|
+
if(code&&code.trim()&&!isSignalsRuntimeScript({hasAttribute:function(name){return name==='data-stx-runtime'&&attrs.indexOf('data-stx-runtime')!==-1}},code)){
|
|
421
|
+
var slot='fragment-'+(++fragScriptId);
|
|
422
|
+
fragScripts.push({text:code,slot:slot,setupName:generatedSetupName(code)});
|
|
423
|
+
return '<scr'+'ipt type="application/stx-pending" data-stx-route-script="'+slot+'"></scr'+'ipt>';
|
|
424
|
+
}
|
|
407
425
|
return '';
|
|
408
426
|
});
|
|
409
427
|
cleanFrag=cleanFrag.replace(new RegExp('<sty'+'le\\\\b([^>]*)>([\\\\s\\\\S]*?)<\\\\/sty'+'le>','gi'),function(m,attrs,css){
|
|
@@ -452,7 +470,9 @@ else {
|
|
|
452
470
|
// Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
|
|
453
471
|
log('[router] frag scripts:', fragScripts.length);
|
|
454
472
|
document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
455
|
-
fragScripts.
|
|
473
|
+
fragScripts.sort(function(a,b){return (a.setupName?1:0)-(b.setupName?1:0)});
|
|
474
|
+
fragScripts.forEach(function(entry){
|
|
475
|
+
var code=entry.text;
|
|
456
476
|
log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
|
|
457
477
|
// Skip scripts that were already executed (layout-level partials
|
|
458
478
|
// like theme.stx, stores.stx, nav.stx). Their top-level const/let
|
|
@@ -478,13 +498,19 @@ else {
|
|
|
478
498
|
}
|
|
479
499
|
executedScriptHashes[h]=1;
|
|
480
500
|
var ns=document.createElement('script');
|
|
481
|
-
var hasImport=code
|
|
501
|
+
var hasImport=hasStaticImport(code);
|
|
482
502
|
if(hasImport){
|
|
483
503
|
ns.type='module';
|
|
484
504
|
}
|
|
485
505
|
ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
|
|
486
506
|
ns.setAttribute('data-stx-page','');
|
|
487
|
-
document.
|
|
507
|
+
var placeholder=document.querySelector('script[data-stx-route-script="'+entry.slot+'"]');
|
|
508
|
+
if(placeholder&&placeholder.parentNode){
|
|
509
|
+
ns.setAttribute('data-stx-positioned','');
|
|
510
|
+
placeholder.parentNode.replaceChild(ns,placeholder);
|
|
511
|
+
}else{
|
|
512
|
+
document.body.appendChild(ns);
|
|
513
|
+
}
|
|
488
514
|
});
|
|
489
515
|
log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);
|
|
490
516
|
// THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
|
|
@@ -593,10 +619,35 @@ else {
|
|
|
593
619
|
var newGroup=getDocLayoutGroup(doc,newLayout);
|
|
594
620
|
isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);
|
|
595
621
|
}
|
|
622
|
+
var incomingSetupName=newBody?(newBody.getAttribute('data-stx')||''):'';
|
|
623
|
+
// Scripts assigned through innerHTML are inert. Preserve executable
|
|
624
|
+
// scripts as placeholders so re-execution keeps document.currentScript
|
|
625
|
+
// beside the component template it owns.
|
|
626
|
+
var routedBodyScripts=[];
|
|
627
|
+
var routedBodyScriptId=0;
|
|
628
|
+
function prepareRoutedBodyScripts(root){
|
|
629
|
+
if(!root)return;
|
|
630
|
+
root.querySelectorAll('script').forEach(function(s){
|
|
631
|
+
var text=s.textContent||'';
|
|
632
|
+
if(s.hasAttribute('src')){s.remove();return}
|
|
633
|
+
var type=(s.getAttribute('type')||'').trim().toLowerCase();
|
|
634
|
+
var executable=!type||type==='text/javascript'||type==='application/javascript'||type==='module';
|
|
635
|
+
if(!executable||!text.trim())return;
|
|
636
|
+
if(isSignalsRuntimeScript(s,text)||text.indexOf('__stxRouter')!==-1){s.remove();return}
|
|
637
|
+
var slot='document-'+(++routedBodyScriptId);
|
|
638
|
+
var setupName=generatedSetupName(text);
|
|
639
|
+
if(setupName)incomingSetupName=setupName;
|
|
640
|
+
routedBodyScripts.push({text:text,runAlways:true,slot:slot,setupName:setupName});
|
|
641
|
+
s.textContent='';
|
|
642
|
+
s.setAttribute('type','application/stx-pending');
|
|
643
|
+
s.setAttribute('data-stx-route-script',slot);
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
prepareRoutedBodyScripts(isLayoutChange?newBody:newContent);
|
|
596
647
|
if(isLayoutChange&&newBody){
|
|
597
648
|
log('[router] full body swap for layout change');
|
|
598
649
|
// Replace entire body content \u2014 layout chrome and all
|
|
599
|
-
var bodyHTML=newBody.innerHTML
|
|
650
|
+
var bodyHTML=newBody.innerHTML;
|
|
600
651
|
document.body.innerHTML=bodyHTML;
|
|
601
652
|
// Copy body attributes (class, data-stx, etc.)
|
|
602
653
|
Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});
|
|
@@ -618,8 +669,9 @@ else {
|
|
|
618
669
|
var attrMap={};
|
|
619
670
|
Array.prototype.forEach.call(newContent.attributes,function(a){attrMap[a.name]=a.value});
|
|
620
671
|
setContainerAttrs(currentContent,attrMap);
|
|
621
|
-
var cleanHTML=newContent.innerHTML
|
|
672
|
+
var cleanHTML=newContent.innerHTML;
|
|
622
673
|
currentContent.innerHTML=cleanHTML;
|
|
674
|
+
if(incomingSetupName)document.body.setAttribute('data-stx',incomingSetupName);
|
|
623
675
|
}
|
|
624
676
|
|
|
625
677
|
// \u2500\u2500 Load new external <head> scripts \u2500\u2500
|
|
@@ -645,40 +697,20 @@ else {
|
|
|
645
697
|
// Remove previously injected page scripts
|
|
646
698
|
document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
647
699
|
|
|
648
|
-
var scripts=
|
|
649
|
-
function addScript(text, runAlways){
|
|
650
|
-
scripts.push({text:text,runAlways:!!runAlways});
|
|
700
|
+
var scripts=routedBodyScripts.slice();
|
|
701
|
+
function addScript(text, runAlways, slot){
|
|
702
|
+
scripts.push({text:text,runAlways:!!runAlways,slot:slot||''});
|
|
651
703
|
}
|
|
652
704
|
if(isLayoutChange){
|
|
653
|
-
// Layout change: collect ALL scripts from the new document body
|
|
654
|
-
// (layout scripts, component mounts, setup functions \u2014 everything)
|
|
655
|
-
var newBodyEl=doc.querySelector('body');
|
|
656
|
-
if(newBodyEl){
|
|
657
|
-
newBodyEl.querySelectorAll('script').forEach(function(s){
|
|
658
|
-
var text=s.textContent||'';
|
|
659
|
-
if(s.hasAttribute('src'))return;
|
|
660
|
-
if(!text.trim())return;
|
|
661
|
-
// Skip the signals runtime IIFE \u2014 it's already loaded
|
|
662
|
-
if(text.indexOf("'use strict';var cloakStyle")!==-1)return;
|
|
663
|
-
if(text.indexOf('__stxRouter')!==-1)return;
|
|
664
|
-
addScript(text,true);
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
705
|
// Also collect setup functions from <head>
|
|
668
706
|
doc.querySelectorAll('head script').forEach(function(s){
|
|
669
707
|
var text=s.textContent||'';
|
|
670
708
|
if(s.hasAttribute('src'))return;
|
|
671
709
|
if(!text.trim())return;
|
|
710
|
+
if(isSignalsRuntimeScript(s,text))return;
|
|
672
711
|
if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);
|
|
673
712
|
});
|
|
674
713
|
} else {
|
|
675
|
-
// Same layout: collect scripts from the container
|
|
676
|
-
newContent.querySelectorAll('script').forEach(function(s){
|
|
677
|
-
var text=s.textContent||'';
|
|
678
|
-
if(s.hasAttribute('src'))return;
|
|
679
|
-
if(!text.trim())return;
|
|
680
|
-
addScript(text,true);
|
|
681
|
-
});
|
|
682
714
|
// Collect page scripts from <head> AND <body> (outside container).
|
|
683
715
|
// SSG builds place the setup function in <body> before <main>, not
|
|
684
716
|
// in <head>. Without this, SPA navigation on static sites never
|
|
@@ -690,16 +722,26 @@ else {
|
|
|
690
722
|
if(s.hasAttribute('src'))return;
|
|
691
723
|
if(!text.trim())return;
|
|
692
724
|
// Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)
|
|
693
|
-
if(text
|
|
725
|
+
if(isSignalsRuntimeScript(s,text))return;
|
|
694
726
|
if(text.indexOf('__stxRouter')!==-1)return;
|
|
695
727
|
if(newContent.contains(s))return;
|
|
696
|
-
if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;
|
|
728
|
+
if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page')&&!s.hasAttribute('data-stx-route-params'))return;
|
|
697
729
|
var key=text.substring(0,80);
|
|
698
730
|
if(seenSetups[key])return;
|
|
699
731
|
seenSetups[key]=1;
|
|
700
732
|
addScript(text,true);
|
|
701
733
|
});
|
|
702
734
|
}
|
|
735
|
+
// Component setup scripts also assign window.stx._latestSetup. Run the
|
|
736
|
+
// destination page setup last so stx:load hydrates with the page scope,
|
|
737
|
+
// not whichever nested component happened to render last.
|
|
738
|
+
if(incomingSetupName){
|
|
739
|
+
scripts.sort(function(a,b){
|
|
740
|
+
var aPage=(a.setupName||generatedSetupName(a.text))===incomingSetupName?1:0;
|
|
741
|
+
var bPage=(b.setupName||generatedSetupName(b.text))===incomingSetupName?1:0;
|
|
742
|
+
return aPage-bPage;
|
|
743
|
+
});
|
|
744
|
+
}
|
|
703
745
|
|
|
704
746
|
// Push history state (before active link updates so location.pathname is current)
|
|
705
747
|
if(pushState!==false)history.pushState({},'',url+(hash||''));
|
|
@@ -738,7 +780,7 @@ else {
|
|
|
738
780
|
// need special handling.
|
|
739
781
|
var h=hashScript(text);
|
|
740
782
|
var isSetup=text.indexOf('__stx_setup_')!==-1;
|
|
741
|
-
var hasImport=text
|
|
783
|
+
var hasImport=hasStaticImport(text);
|
|
742
784
|
if(!runAlways&&!isSetup&&executedScriptHashes[h])return;
|
|
743
785
|
executedScriptHashes[h]=1;
|
|
744
786
|
// Wrap scripts with import statements as modules (Bug 3 fix)
|
|
@@ -755,7 +797,13 @@ else {
|
|
|
755
797
|
var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;
|
|
756
798
|
ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
|
|
757
799
|
ns.setAttribute('data-stx-page','');
|
|
758
|
-
document.
|
|
800
|
+
var placeholder=entry.slot?document.querySelector('script[data-stx-route-script="'+entry.slot+'"]'):null;
|
|
801
|
+
if(placeholder&&placeholder.parentNode){
|
|
802
|
+
ns.setAttribute('data-stx-positioned','');
|
|
803
|
+
placeholder.parentNode.replaceChild(ns,placeholder);
|
|
804
|
+
}else{
|
|
805
|
+
document.body.appendChild(ns);
|
|
806
|
+
}
|
|
759
807
|
});
|
|
760
808
|
// THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
|
|
761
809
|
window.dispatchEvent(new Event('stx:load'));
|
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.115",
|
|
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",
|