bun-plugin-stx 0.2.46 → 0.2.51

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.
@@ -1,4 +1,8 @@
1
1
  // @bun
2
+ import {
3
+ deriveLayoutGroup,
4
+ extractLayoutMetadata
5
+ } from "./chunk-93ete56h.js";
2
6
  import"./chunk-2mx7fq49.js";
3
7
 
4
8
  // ../router/src/file-router.ts
@@ -153,25 +157,35 @@ function scanDirectory(dir, extensions) {
153
157
  class Router {
154
158
  routes;
155
159
  pagesDir;
160
+ pagesDirs;
156
161
  config;
157
162
  constructor(baseDir, config = {}) {
158
163
  this.config = config;
159
- this.pagesDir = path4.resolve(baseDir, config.pagesDir || "pages");
164
+ const rawDirs = config.pagesDirs && config.pagesDirs.length > 0 ? config.pagesDirs : [config.pagesDir || "pages"];
165
+ this.pagesDirs = rawDirs.map((d) => path4.resolve(baseDir, d));
166
+ this.pagesDir = this.pagesDirs[0];
160
167
  const extensions = config.extensions || [".stx"];
161
- const files = scanDirectory(this.pagesDir, extensions);
162
- this.routes = files.map((filePath) => {
163
- const pattern = filePathToPattern(filePath, this.pagesDir);
164
- const { regex, params } = patternToRegex(pattern);
165
- const layouts = config.layouts !== false ? resolveLayoutChain(filePath, this.pagesDir) : [];
166
- return {
167
- pattern,
168
- regex,
169
- params,
170
- filePath,
171
- isDynamic: params.length > 0,
172
- layout: layouts.length > 0 ? layouts[layouts.length - 1] : undefined
173
- };
174
- });
168
+ const seenPatterns = new Set;
169
+ this.routes = [];
170
+ for (const dir of this.pagesDirs) {
171
+ const files = scanDirectory(dir, extensions);
172
+ for (const filePath of files) {
173
+ const pattern = filePathToPattern(filePath, dir);
174
+ if (seenPatterns.has(pattern))
175
+ continue;
176
+ seenPatterns.add(pattern);
177
+ const { regex, params } = patternToRegex(pattern);
178
+ const layouts = config.layouts !== false ? resolveLayoutChain(filePath, dir) : [];
179
+ this.routes.push({
180
+ pattern,
181
+ regex,
182
+ params,
183
+ filePath,
184
+ isDynamic: params.length > 0,
185
+ layout: layouts.length > 0 ? layouts[layouts.length - 1] : undefined
186
+ });
187
+ }
188
+ }
175
189
  this.routes.sort((a, b) => {
176
190
  if (!a.isDynamic && b.isDynamic)
177
191
  return -1;
@@ -575,6 +589,8 @@ function getRouterScript() {
575
589
  var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false};
576
590
  var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
577
591
  var containerSel=o.container;
592
+ var debug=!!o.debug;
593
+ function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}
578
594
 
579
595
  // \u2500\u2500 Progress bar (0\u2192100% at top of viewport) \u2500\u2500
580
596
  // Native loading indicator baked into the router. Starts when navigation
@@ -666,6 +682,7 @@ function getRouterScript() {
666
682
  // Layout group detection \u2014 layouts in the same group share a <main> container.
667
683
  // Only truly different layout groups trigger a full page reload.
668
684
  var layoutCache={};
685
+ var layoutGroupCache={};
669
686
  // Track executed script hashes to prevent redeclaration errors on navigation.
670
687
  // Layout-level scripts (theme, nav setup) execute on initial page load and
671
688
  // should NOT re-execute when navigating to another page with the same layout.
@@ -679,33 +696,50 @@ function getRouterScript() {
679
696
  var text=s.textContent||'';
680
697
  if(text.trim()&&!s.hasAttribute('src'))executedScriptHashes[hashScript(text)]=1;
681
698
  });
682
- function getLayoutGroup(layout){
699
+ function cacheKey(url){
700
+ var u=new URL(url,location.origin);
701
+ return u.pathname+u.search;
702
+ }
703
+ function defaultLayoutGroup(layout){
683
704
  if(!layout)return 'app';
684
- if(layout.indexOf('auth')!==-1||layout.indexOf('guest')!==-1)return 'auth';
685
- return 'app';
705
+ var clean=String(layout).replace(/\\\\/g,'/');
706
+ var parts=clean.split('/').filter(Boolean);
707
+ var idx=parts.lastIndexOf('layouts');
708
+ var part=idx>=0?parts[idx+1]:parts[parts.length-1];
709
+ return part?part.replace(/\\.stx$/i,''):'app';
710
+ }
711
+ function getCurrentLayoutGroup(){
712
+ var meta=document.querySelector('meta[name="stx-layout-group"]');
713
+ if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');
714
+ var layout=document.querySelector('meta[name="stx-layout"]');
715
+ return defaultLayoutGroup(layout?layout.getAttribute('content'):'');
716
+ }
717
+ function getDocLayoutGroup(doc,layout){
718
+ var meta=doc&&doc.querySelector?doc.querySelector('meta[name="stx-layout-group"]'):null;
719
+ return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');
686
720
  }
687
- function checkLayoutChange(newLayout,targetUrl){
721
+ function checkLayoutChange(newLayout,targetUrl,newGroup){
688
722
  var currentLayout=document.querySelector('meta[name="stx-layout"]');
689
723
  var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';
690
- var curGroup=getLayoutGroup(curLayoutName);
691
- var newGroup=getLayoutGroup(newLayout);
692
- console.log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+newGroup+')');
724
+ var curGroup=getCurrentLayoutGroup();
725
+ var nextGroup=newGroup||defaultLayoutGroup(newLayout);
726
+ log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');
693
727
  // Different layout GROUP (e.g. app \u2192 auth): full reload
694
- if(curGroup!==newGroup){
695
- console.log('[router] layout group change:',curGroup,'\u2192',newGroup,'\u2014 full reload to:',targetUrl);
728
+ if(curGroup!==nextGroup){
729
+ log('[router] layout group change:',curGroup,'\u2192',nextGroup,'\u2014 full reload to:',targetUrl);
696
730
  return true;
697
731
  }
698
732
  // Same group but different SPECIFIC layout (e.g. layouts/app \u2192 layouts/coach):
699
733
  // full body swap so nav, sidebar, and other layout-level elements update
700
734
  if(curLayoutName && newLayout && curLayoutName!==newLayout){
701
- console.log('[router] layout name change:',curLayoutName,'\u2192',newLayout,'\u2014 full body swap');
735
+ log('[router] layout name change:',curLayoutName,'\u2192',newLayout,'\u2014 full body swap');
702
736
  return true;
703
737
  }
704
738
  return false;
705
739
  }
706
740
 
707
741
  function navigate(url,pushState,force){
708
- console.log('[router] navigate() called:',url,'isNavigating:',isNavigating);
742
+ log('[router] navigate() called:',url,'isNavigating:',isNavigating);
709
743
  if(isNavigating)return;
710
744
  var t=new URL(url,location.origin);
711
745
 
@@ -718,27 +752,33 @@ function getRouterScript() {
718
752
  return;
719
753
  }
720
754
 
721
- if(t.href===location.href&&!t.hash&&!force)return;
755
+ // Skip if user clicks a link to the page they're already on. We
756
+ // deliberately allow popstate (pushState===false) through because
757
+ // the browser has already updated location.href to the popped
758
+ // entry \u2014 without this allowance, hitting back would early-return
759
+ // and the visible page content would be left frozen on the
760
+ // forward-navigation page.
761
+ if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return;
722
762
 
723
763
  isNavigating=true;
724
764
  document.body.classList.add(o.loadingClass);
725
765
  startProgress();
726
766
 
727
767
  var targetHref=t.href;
728
- var targetPath=t.pathname;
768
+ var targetPath=cacheKey(url);
729
769
  var targetHash=t.hash;
730
770
 
731
771
  function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}
732
772
 
733
773
  if(o.cache&&cache[targetPath]&&!force){
734
- if(checkLayoutChange(layoutCache[targetPath],url)){
774
+ if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){
735
775
  // Layout changed \u2014 fetch full page and do full document swap
736
- console.log('[router] cache hit but layout changed \u2014 fetching full page');
776
+ log('[router] cache hit but layout changed \u2014 fetching full page');
737
777
  fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){
738
778
  if(!r.ok)throw new Error(r.status);
739
779
  return r.text();
740
780
  }).then(function(html){
741
- console.log('[router] full page fetched from cache path, len:',html.length);
781
+ log('[router] full page fetched from cache path, len:',html.length);
742
782
  swap(html,targetPath,pushState,targetHash);
743
783
  }).catch(function(err){
744
784
  console.error('[router] full page fetch error:',err);
@@ -756,23 +796,24 @@ else {
756
796
  if(!r.ok)throw new Error(r.status);
757
797
  var isFragment=r.headers.get('X-STX-Fragment')==='true';
758
798
  var newLayout=r.headers.get('X-STX-Layout')||'';
799
+ var newGroup=r.headers.get('X-STX-Layout-Group')||'';
759
800
  // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap
760
- if(isFragment&&checkLayoutChange(newLayout,url)){
761
- console.log('[router] layout change \u2014 fetching full page for document swap');
801
+ if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){
802
+ log('[router] layout change \u2014 fetching full page for document swap');
762
803
  return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){
763
- console.log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);
804
+ log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);
764
805
  if(!fullRes.ok)throw new Error(fullRes.status);
765
806
  return fullRes.text().then(function(html){
766
- console.log('[router] full page html length:',html.length);
767
- return{html:html,isFragment:false,layout:newLayout};
807
+ log('[router] full page html length:',html.length);
808
+ return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup};
768
809
  });
769
810
  });
770
811
  }
771
- return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout}});
812
+ return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup}});
772
813
  }).then(function(result){
773
814
  if(!result)return;
774
815
  if(result.isFragment)result.html='<!--stx-fragment-->'+result.html;
775
- if(o.cache){cache[targetPath]=result.html;layoutCache[targetPath]=result.layout}
816
+ if(o.cache){cache[targetPath]=result.html;layoutCache[targetPath]=result.layout;layoutGroupCache[targetPath]=result.layoutGroup}
776
817
  swap(result.html,targetPath,pushState,targetHash);
777
818
  }).catch(function(err){
778
819
  console.error('[router] fetch error:',err);
@@ -785,8 +826,8 @@ else {
785
826
  var isFragment=html.indexOf('<!--stx-fragment-->')===0;
786
827
  if(isFragment)html=html.slice('<!--stx-fragment-->'.length);
787
828
  var currentContent=getContainer();
788
- console.log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' htmlLen='+html.length);
789
- if(!currentContent){console.log('[router] no container \u2014 falling back');location.href=url;return}
829
+ log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&&currentContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);
830
+ if(!currentContent){log('[router] no container \u2014 falling back');location.href=url;return}
790
831
 
791
832
  // Fragment mode: server returned just the page content (no document wrapper)
792
833
  if(isFragment){
@@ -841,10 +882,22 @@ else {
841
882
  else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
842
883
  window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
843
884
  // Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
844
- console.log('[router] frag scripts:', fragScripts.length);
885
+ log('[router] frag scripts:', fragScripts.length);
845
886
  document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
846
887
  fragScripts.forEach(function(code){
847
- console.log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
888
+ log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
889
+ // Skip scripts that were already executed (layout-level partials
890
+ // like theme.stx, stores.stx, nav.stx). Their top-level const/let
891
+ // declarations would throw "Identifier has already been declared"
892
+ // on re-execution. Setup functions (__stx_setup_) must always
893
+ // re-execute because each page has its own setup.
894
+ var h=hashScript(code);
895
+ var isSetup=code.indexOf('__stx_setup_')!==-1;
896
+ if(!isSetup&&executedScriptHashes[h]){
897
+ log('[router] skipping already-executed script (hash dedup)');
898
+ return;
899
+ }
900
+ executedScriptHashes[h]=1;
848
901
  var ns=document.createElement('script');
849
902
  // Wrap in block scope to prevent const/let collisions on re-navigation.
850
903
  // Without this, navigating away and back throws "Identifier has already
@@ -855,7 +908,7 @@ else {
855
908
  ns.setAttribute('data-stx-page','');
856
909
  document.body.appendChild(ns);
857
910
  });
858
- console.log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);
911
+ log('[router] scripts done. _latestSetup:', !!window.stx._latestSetup);
859
912
  // THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
860
913
  window.dispatchEvent(new Event('stx:load'));
861
914
  }
@@ -927,12 +980,14 @@ else {
927
980
  if(newBody){
928
981
  var newMeta=doc.querySelector('meta[name="stx-layout"]');
929
982
  var curMeta=document.querySelector('meta[name="stx-layout"]');
930
- var curGroup=getLayoutGroup(curMeta?curMeta.getAttribute('content'):'');
931
- var newGroup=getLayoutGroup(newMeta?newMeta.getAttribute('content'):'');
932
- isLayoutChange=curGroup!==newGroup;
983
+ var curLayout=curMeta?curMeta.getAttribute('content'):'';
984
+ var newLayout=newMeta?newMeta.getAttribute('content'):'';
985
+ var curGroup=getCurrentLayoutGroup();
986
+ var newGroup=getDocLayoutGroup(doc,newLayout);
987
+ isLayoutChange=curGroup!==newGroup||(curLayout&&newLayout&&curLayout!==newLayout);
933
988
  }
934
989
  if(isLayoutChange&&newBody){
935
- console.log('[router] full body swap for layout change');
990
+ log('[router] full body swap for layout change');
936
991
  // Replace entire body content \u2014 layout chrome and all
937
992
  var bodyHTML=newBody.innerHTML.replace(new RegExp('<scr'+'ipt\\\\b[^>]*>[\\\\s\\\\S]*?<\\\\/scr'+'ipt\\\\s*>','gi'),'');
938
993
  document.body.innerHTML=bodyHTML;
@@ -943,6 +998,10 @@ else {
943
998
  var freshMeta=doc.querySelector('meta[name="stx-layout"]');
944
999
  if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');
945
1000
  else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}
1001
+ var oldGroupMeta=document.querySelector('meta[name="stx-layout-group"]');
1002
+ var freshGroupMeta=doc.querySelector('meta[name="stx-layout-group"]');
1003
+ if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');
1004
+ else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}
946
1005
  // Update container reference for script execution below
947
1006
  currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;
948
1007
  } else {
@@ -1130,12 +1189,12 @@ else {
1130
1189
  }
1131
1190
  if(!link){return}
1132
1191
  var href=link.getAttribute('href');
1133
- console.log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);
1134
- if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){console.log('[router] skipped:',href);return}
1192
+ log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);
1193
+ if(!href||href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')){log('[router] skipped:',href);return}
1135
1194
  if(link.target==='_blank'||link.hasAttribute('download'))return;
1136
1195
  e.preventDefault();
1137
1196
  e.stopPropagation();
1138
- console.log('[router] navigating to:',href);
1197
+ log('[router] navigating to:',href);
1139
1198
  navigate(href);
1140
1199
  },true);
1141
1200
 
@@ -1151,15 +1210,17 @@ else {
1151
1210
  var link=e.target.closest('[data-stx-link]');
1152
1211
  if(!link)return;
1153
1212
  var href=link.getAttribute('href');
1154
- if(cache[href]||prefetching[href])return;
1155
- prefetching[href]=true;
1213
+ var key=cacheKey(href);
1214
+ if(cache[key]||prefetching[key])return;
1215
+ prefetching[key]=true;
1156
1216
  fetch(href,{headers:{'X-STX-Router':'true','Accept':'text/html'}}).then(function(r){
1157
1217
  var isFrag=r.headers.get('X-STX-Fragment')==='true';
1158
1218
  var pLayout=r.headers.get('X-STX-Layout')||'';
1159
- return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout}});
1219
+ var pGroup=r.headers.get('X-STX-Layout-Group')||'';
1220
+ return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});
1160
1221
  }).then(function(result){
1161
- if(o.cache){cache[href]=result.html;layoutCache[href]=result.layout}
1162
- }).catch(function(){}).finally(function(){delete prefetching[href]});
1222
+ if(o.cache){cache[key]=result.html;layoutCache[key]=result.layout;layoutGroupCache[key]=result.layoutGroup}
1223
+ }).catch(function(){}).finally(function(){delete prefetching[key]});
1163
1224
  },true);
1164
1225
  }
1165
1226
 
@@ -1252,11 +1313,12 @@ else {
1252
1313
  navigate:navigate,
1253
1314
  navigateTo:navigate,
1254
1315
  prefetch:function(url){
1255
- if(!cache[url]){
1256
- fetch(url,{headers:{'X-STX-Router':'true'}}).then(function(r){var isFrag=r.headers.get('X-STX-Fragment')==='true';return r.text().then(function(html){return isFrag?'<!--stx-fragment-->'+html:html})}).then(function(html){cache[url]=html}).catch(function(){});
1316
+ var key=cacheKey(url);
1317
+ if(!cache[key]){
1318
+ fetch(url,{headers:{'X-STX-Router':'true'}}).then(function(r){var isFrag=r.headers.get('X-STX-Fragment')==='true';var pLayout=r.headers.get('X-STX-Layout')||'';var pGroup=r.headers.get('X-STX-Layout-Group')||'';return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}})}).then(function(result){cache[key]=result.html;layoutCache[key]=result.layout;layoutGroupCache[key]=result.layoutGroup}).catch(function(){});
1257
1319
  }
1258
1320
  },
1259
- clearCache:function(){for(var k in cache)delete cache[k]},
1321
+ clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk]},
1260
1322
  cache:cache,
1261
1323
  swap:swap,
1262
1324
  updateNav:updateNav
@@ -1298,6 +1360,8 @@ export {
1298
1360
  formatRoutes,
1299
1361
  findErrorPage,
1300
1362
  filePathToPattern,
1363
+ extractLayoutMetadata,
1364
+ deriveLayoutGroup,
1301
1365
  defineRoutes,
1302
1366
  defineRoute,
1303
1367
  defineMiddleware,
@@ -0,0 +1,23 @@
1
+ // @bun
2
+ // ../router/src/layout-metadata.ts
3
+ function deriveLayoutGroup(layoutPath) {
4
+ const normalized = layoutPath.replace(/\\/g, "/");
5
+ const segments = normalized.split("/").filter(Boolean);
6
+ const layoutsIndex = segments.lastIndexOf("layouts");
7
+ const layoutSegment = layoutsIndex >= 0 ? segments[layoutsIndex + 1] : segments.at(-1);
8
+ if (!layoutSegment)
9
+ return "app";
10
+ return layoutSegment.replace(/\.stx$/i, "") || "app";
11
+ }
12
+ function extractLayoutMetadata(html) {
13
+ const layoutMeta = html.match(/<meta\b[^>]*name=["']stx-layout["'][^>]*content=["']([^"']+)["'][^>]*>/i);
14
+ const groupMeta = html.match(/<meta\b[^>]*name=["']stx-layout-group["'][^>]*content=["']([^"']+)["'][^>]*>/i);
15
+ const layoutComment = html.match(/<!--\s*stx-layout:\s*([^ ]+)\s*-->/i);
16
+ const layout = layoutMeta?.[1] || layoutComment?.[1] || "";
17
+ return {
18
+ layout,
19
+ group: groupMeta?.[1] || deriveLayoutGroup(layout)
20
+ };
21
+ }
22
+
23
+ export { deriveLayoutGroup, extractLayoutMetadata };
package/dist/serve.js CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ import {
4
+ deriveLayoutGroup
5
+ } from "./chunk-93ete56h.js";
3
6
  import {
4
7
  __require
5
8
  } from "./chunk-2mx7fq49.js";
@@ -8956,9 +8959,9 @@ After installing: bun add bun-plugin-stx`);
8956
8959
  pageMiddlewarePatterns.length = 0;
8957
8960
  await Promise.all(files.map((f) => detectPageMiddleware(f)));
8958
8961
  try {
8959
- const { Router } = await import("./chunk-nyzhqegd.js");
8960
- const pagesDir = patterns[0]?.replace(/\/$/, "") || "pages";
8961
- const router = new Router(process19.cwd(), { pagesDir });
8962
+ const { Router } = await import("./chunk-0nwya50v.js");
8963
+ const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
8964
+ const router = new Router(process19.cwd(), { pagesDirs });
8962
8965
  console.log(`[stx] Generated ${router.routes.length} routes \u2192 .stx/routes.ts`);
8963
8966
  } catch (e) {}
8964
8967
  return files;
@@ -9540,6 +9543,7 @@ ${output}`;
9540
9543
  if (isSpaNav) {
9541
9544
  const layoutMatch = content.match(/<!-- stx-layout: ([^ ]+) -->/);
9542
9545
  const pageLayout = layoutMatch ? layoutMatch[1] : "default";
9546
+ const pageLayoutGroup = deriveLayoutGroup(pageLayout);
9543
9547
  let fragment = content;
9544
9548
  const headStyles = [];
9545
9549
  const headMatch = content.match(/<head\b[^>]*>([\s\S]*?)<\/head>/i);
@@ -9608,6 +9612,7 @@ ${pageSetupScripts.join(`
9608
9612
  "Content-Type": "text/html; charset=utf-8",
9609
9613
  "X-STX-Fragment": "true",
9610
9614
  "X-STX-Layout": pageLayout,
9615
+ "X-STX-Layout-Group": pageLayoutGroup,
9611
9616
  "Cache-Control": "no-cache",
9612
9617
  ...corsHeaders
9613
9618
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-plugin-stx",
3
3
  "type": "module",
4
- "version": "0.2.46",
4
+ "version": "0.2.51",
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",
@@ -43,8 +43,9 @@
43
43
  "import": "./dist/serve.js"
44
44
  },
45
45
  "./*": {
46
- "bun": "./dist/*",
47
- "import": "./dist/*"
46
+ "bun": "./dist/*.js",
47
+ "types": "./dist/*.d.ts",
48
+ "import": "./dist/*.js"
48
49
  }
49
50
  },
50
51
  "module": "./dist/index.js",
@@ -72,6 +73,7 @@
72
73
  "typecheck": "bun tsc --noEmit"
73
74
  },
74
75
  "dependencies": {
75
- "@stacksjs/stx": "0.2.46"
76
+ "@stacksjs/stx": "0.2.51",
77
+ "stx-router": "0.2.51"
76
78
  }
77
79
  }