stx-router 0.2.287 → 0.2.289

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.
Files changed (2) hide show
  1. package/dist/client.js +30 -5
  2. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -322,6 +322,23 @@ export function getRouterScript() {
322
322
  var head=code.trimStart();
323
323
  return head.charAt(0)==='('||head.charAt(0)===';';
324
324
  }
325
+ // A component scope script names the root it binds in data-stx-owner (#1958).
326
+ // On navigation it runs only for a root that just ARRIVED: one swapped in
327
+ // with the new content, which is not bound yet. A layout component that
328
+ // stayed on screen is skipped, in both shapes that reach here:
329
+ // - its root is gone: it keeps its first render's id while the fragment
330
+ // carries the next page's id for it (ids are page-keyed on purpose), so
331
+ // the script has no markup of its own;
332
+ // - its root is present and already bound: the same page file on both
333
+ // sides of the navigation gives the same id.
334
+ // Either way running it built a second instance of a component whose markup
335
+ // stays bound to the first -- setup and its side effects repeated on every
336
+ // navigation. Scripts without an owner are unaffected.
337
+ function ownerStays(owner){
338
+ if(!owner)return false;
339
+ var root=document.querySelector('[data-stx-scope="'+owner+'"]');
340
+ return !root||typeof root.__stx_disposers==='function';
341
+ }
325
342
  function hashScript(code){
326
343
  var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}
327
344
  return h;
@@ -819,7 +836,8 @@ else {
819
836
  var slot='fragment-'+(++fragScriptId);
820
837
  var scoped=/(?:^|\\s)data-stx-scoped(?:\\s|=|$)/i.test(attrs);
821
838
  var runDecl=(attrs.match(/data-stx-run\\s*=\\s*["']?(always|once)["']?/i)||[])[1];
822
- fragScripts.push({text:code,slot:slot,setupName:generatedSetupName(code),scoped:scoped,run:runDecl?runDecl.toLowerCase():''});
839
+ var ownerDecl=(attrs.match(/data-stx-owner\\s*=\\s*["']([^"']+)["']/i)||[])[1]||'';
840
+ fragScripts.push({text:code,slot:slot,setupName:generatedSetupName(code),scoped:scoped,run:runDecl?runDecl.toLowerCase():'',owner:ownerDecl});
823
841
  // Retain scoped setup code in its inert placeholder. A placeholder
824
842
  // inside template.content is unreachable through document, so the
825
843
  // repeated component runtime must execute it for each clone.
@@ -934,6 +952,10 @@ else {
934
952
  // declarations would throw "Identifier has already been declared"
935
953
  // on re-execution. Setup functions (__stx_setup_) must always
936
954
  // re-execute because each page has its own setup.
955
+ if(ownerStays(entry.owner)){
956
+ log('[router] skipping component that stayed on the page:', entry.owner);
957
+ return;
958
+ }
937
959
  var h=hashScript(code);
938
960
  var isSetup=code.indexOf('__stx_setup_')!==-1;
939
961
  // Self-contained scope scripts must re-execute on every navigation:
@@ -1157,7 +1179,7 @@ else {
1157
1179
  var slot='document-'+(++routedBodyScriptId);
1158
1180
  var setupName=generatedSetupName(text);
1159
1181
  if(setupName)incomingSetupName=setupName;
1160
- routedBodyScripts.push({text:text,runAlways:true,slot:slot,setupName:setupName,run:s.getAttribute('data-stx-run')||''});
1182
+ routedBodyScripts.push({text:text,runAlways:true,slot:slot,setupName:setupName,run:s.getAttribute('data-stx-run')||'',owner:s.getAttribute('data-stx-owner')||''});
1161
1183
  s.textContent='';
1162
1184
  s.setAttribute('type','application/stx-pending');
1163
1185
  s.setAttribute('data-stx-route-script',slot);
@@ -1226,8 +1248,8 @@ else {
1226
1248
  document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1227
1249
 
1228
1250
  var scripts=routedBodyScripts.slice();
1229
- function addScript(text, runAlways, slot){
1230
- scripts.push({text:text,runAlways:!!runAlways,slot:slot||''});
1251
+ function addScript(text, runAlways, slot, owner){
1252
+ scripts.push({text:text,runAlways:!!runAlways,slot:slot||'',owner:owner||''});
1231
1253
  }
1232
1254
  if(isLayoutChange){
1233
1255
  // Also collect setup functions from <head>
@@ -1257,7 +1279,9 @@ else {
1257
1279
  var key=text.substring(0,80);
1258
1280
  if(seenSetups[key])return;
1259
1281
  seenSetups[key]=1;
1260
- addScript(text,true);
1282
+ // A layout component outside the container carries its owner, so
1283
+ // execScripts skips it when it stayed on the page (#1958).
1284
+ addScript(text,true,'',s.getAttribute('data-stx-owner')||'');
1261
1285
  });
1262
1286
  }
1263
1287
  // Component setup scripts also assign window.stx._latestSetup. Run the
@@ -1328,6 +1352,7 @@ else {
1328
1352
  scripts.forEach(function(entry){
1329
1353
  var text=typeof entry==='string'?entry:entry.text;
1330
1354
  var runAlways=typeof entry==='string'?false:entry.runAlways;
1355
+ if(typeof entry!=='string'&&ownerStays(entry.owner))return;
1331
1356
  // Skip scripts that were already executed (layout-level partials
1332
1357
  // like theme.stx). Their top-level const/function declarations
1333
1358
  // would throw "Identifier has already been declared" on re-execution.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stx-router",
3
3
  "type": "module",
4
- "version": "0.2.287",
4
+ "version": "0.2.289",
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",