bun-plugin-stx 0.2.51 → 0.2.57

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.
@@ -591,6 +591,19 @@ function getRouterScript() {
591
591
  var containerSel=o.container;
592
592
  var debug=!!o.debug;
593
593
  function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}
594
+ function runViewTransition(callback){
595
+ if(!o.viewTransitions||!document.startViewTransition)return false;
596
+ try{
597
+ var transition=document.startViewTransition(callback);
598
+ var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};
599
+ if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);
600
+ if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);
601
+ return true;
602
+ }catch(err){
603
+ log('[router] view transition unavailable:',err&&err.message?err.message:err);
604
+ return false;
605
+ }
606
+ }
594
607
 
595
608
  // \u2500\u2500 Progress bar (0\u2192100% at top of viewport) \u2500\u2500
596
609
  // Native loading indicator baked into the router. Starts when navigation
@@ -738,6 +751,10 @@ function getRouterScript() {
738
751
  return false;
739
752
  }
740
753
 
754
+ function shouldUseFragmentResponse(){
755
+ return containerSel==='main'||containerSel==='[data-stx-content]';
756
+ }
757
+
741
758
  function navigate(url,pushState,force){
742
759
  log('[router] navigate() called:',url,'isNavigating:',isNavigating);
743
760
  if(isNavigating)return;
@@ -791,10 +808,13 @@ function getRouterScript() {
791
808
  }
792
809
  else {
793
810
  if(force&&cache[targetPath])delete cache[targetPath];
794
- // First fetch as fragment (SPA mode)
795
- fetch(url,{headers:{'X-STX-Router':'true','Accept':'text/html'}}).then(function(r){
811
+ // First fetch as fragment (SPA mode) when the configured container is
812
+ // the page content. Custom app-shell containers need full documents so
813
+ // the router does not inject a <main> fragment into the wrong element.
814
+ var wantsFragment=shouldUseFragmentResponse();
815
+ fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
796
816
  if(!r.ok)throw new Error(r.status);
797
- var isFragment=r.headers.get('X-STX-Fragment')==='true';
817
+ var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
798
818
  var newLayout=r.headers.get('X-STX-Layout')||'';
799
819
  var newGroup=r.headers.get('X-STX-Layout-Group')||'';
800
820
  // Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap
@@ -899,12 +919,18 @@ else {
899
919
  }
900
920
  executedScriptHashes[h]=1;
901
921
  var ns=document.createElement('script');
922
+ var hasImport=code.indexOf('import ')!==-1;
923
+ if(hasImport){
924
+ ns.type='module';
925
+ }
902
926
  // Wrap in block scope to prevent const/let collisions on re-navigation.
903
927
  // Without this, navigating away and back throws "Identifier has already
904
928
  // been declared" because the previous page's top-level const/let are
905
- // still in the global scope.
929
+ // still in the global scope. Skip the wrap for module scripts \u2014
930
+ // they get their own scope from ESM, and top-level 'import' is
931
+ // illegal inside a block, which would throw SyntaxError.
906
932
  var isAlreadyScoped=code.trimStart().charAt(0)==='('||code.trimStart().charAt(0)===';'||code.indexOf('window.stx.mount')>-1;
907
- ns.textContent=isAlreadyScoped?code:'{'+code+'}';
933
+ ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
908
934
  ns.setAttribute('data-stx-page','');
909
935
  document.body.appendChild(ns);
910
936
  });
@@ -912,7 +938,7 @@ else {
912
938
  // THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
913
939
  window.dispatchEvent(new Event('stx:load'));
914
940
  }
915
- if(o.viewTransitions&&document.startViewTransition){document.startViewTransition(doFragSwap)}
941
+ if(runViewTransition(doFragSwap)){}
916
942
  else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){doFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}
917
943
  return;
918
944
  }
@@ -1032,6 +1058,9 @@ else {
1032
1058
  document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
1033
1059
 
1034
1060
  var scripts=[];
1061
+ function addScript(text, runAlways){
1062
+ scripts.push({text:text,runAlways:!!runAlways});
1063
+ }
1035
1064
  if(isLayoutChange){
1036
1065
  // Layout change: collect ALL scripts from the new document body
1037
1066
  // (layout scripts, component mounts, setup functions \u2014 everything)
@@ -1044,7 +1073,7 @@ else {
1044
1073
  // Skip the signals runtime IIFE \u2014 it's already loaded
1045
1074
  if(text.indexOf("'use strict';var cloakStyle")!==-1)return;
1046
1075
  if(text.indexOf('__stxRouter')!==-1)return;
1047
- scripts.push(text);
1076
+ addScript(text,true);
1048
1077
  });
1049
1078
  }
1050
1079
  // Also collect setup functions from <head>
@@ -1052,7 +1081,7 @@ else {
1052
1081
  var text=s.textContent||'';
1053
1082
  if(s.hasAttribute('src'))return;
1054
1083
  if(!text.trim())return;
1055
- if(text.indexOf('__stx_setup_')!==-1)scripts.push(text);
1084
+ if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);
1056
1085
  });
1057
1086
  } else {
1058
1087
  // Same layout: collect scripts from the container
@@ -1060,26 +1089,27 @@ else {
1060
1089
  var text=s.textContent||'';
1061
1090
  if(s.hasAttribute('src'))return;
1062
1091
  if(!text.trim())return;
1063
- scripts.push(text);
1092
+ addScript(text,true);
1064
1093
  });
1065
- // Collect setup scripts from <head> AND <body> (outside container).
1094
+ // Collect page scripts from <head> AND <body> (outside container).
1066
1095
  // SSG builds place the setup function in <body> before <main>, not
1067
1096
  // in <head>. Without this, SPA navigation on static sites never
1068
1097
  // runs the setup and reactive data stays empty.
1069
1098
  var seenSetups={};
1070
- scripts.forEach(function(t){if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});
1099
+ scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});
1071
1100
  doc.querySelectorAll('script').forEach(function(s){
1072
1101
  var text=s.textContent||'';
1073
1102
  if(s.hasAttribute('src'))return;
1074
1103
  if(!text.trim())return;
1075
- if(text.indexOf('__stx_setup_')===-1)return;
1076
1104
  // Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)
1077
1105
  if(text.indexOf("'use strict';var cloakStyle")!==-1)return;
1078
1106
  if(text.indexOf('__stxRouter')!==-1)return;
1107
+ if(newContent.contains(s))return;
1108
+ if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page'))return;
1079
1109
  var key=text.substring(0,80);
1080
1110
  if(seenSetups[key])return;
1081
1111
  seenSetups[key]=1;
1082
- scripts.push(text);
1112
+ addScript(text,true);
1083
1113
  });
1084
1114
  }
1085
1115
 
@@ -1109,7 +1139,9 @@ else {
1109
1139
 
1110
1140
  // Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
1111
1141
  function execScripts(){
1112
- scripts.forEach(function(text){
1142
+ scripts.forEach(function(entry){
1143
+ var text=typeof entry==='string'?entry:entry.text;
1144
+ var runAlways=typeof entry==='string'?false:entry.runAlways;
1113
1145
  // Skip scripts that were already executed (layout-level partials
1114
1146
  // like theme.stx). Their top-level const/function declarations
1115
1147
  // would throw "Identifier has already been declared" on re-execution.
@@ -1119,7 +1151,7 @@ else {
1119
1151
  var h=hashScript(text);
1120
1152
  var isSetup=text.indexOf('__stx_setup_')!==-1;
1121
1153
  var hasImport=text.indexOf('import ')!==-1;
1122
- if(!isSetup&&executedScriptHashes[h])return;
1154
+ if(!runAlways&&!isSetup&&executedScriptHashes[h])return;
1123
1155
  executedScriptHashes[h]=1;
1124
1156
  // Wrap scripts with import statements as modules (Bug 3 fix)
1125
1157
  var ns=document.createElement('script');
@@ -1129,8 +1161,11 @@ else {
1129
1161
  // Wrap in block scope to prevent const/let collisions on re-navigation.
1130
1162
  // Top-level const/let in <script> tags are global-scoped in browsers
1131
1163
  // and throw "Identifier has already been declared" on re-execution.
1164
+ // Skip the wrap for module scripts \u2014 they get their own scope from
1165
+ // ESM, and top-level 'import' is illegal inside a block, which
1166
+ // would throw SyntaxError before the script ever runs.
1132
1167
  var alreadyScoped=text.trimStart().charAt(0)==='('||text.trimStart().charAt(0)===';'||text.indexOf('window.stx.mount')>-1;
1133
- ns.textContent=alreadyScoped?text:'{'+text+'}';
1168
+ ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
1134
1169
  ns.setAttribute('data-stx-page','');
1135
1170
  document.body.appendChild(ns);
1136
1171
  });
@@ -1146,8 +1181,7 @@ else {
1146
1181
  }
1147
1182
  }
1148
1183
 
1149
- if(o.viewTransitions&&document.startViewTransition){
1150
- document.startViewTransition(doSwap);
1184
+ if(runViewTransition(doSwap)){
1151
1185
  }
1152
1186
  else {
1153
1187
  // Fallback fade for browsers without View Transitions API
@@ -1213,8 +1247,9 @@ else {
1213
1247
  var key=cacheKey(href);
1214
1248
  if(cache[key]||prefetching[key])return;
1215
1249
  prefetching[key]=true;
1216
- fetch(href,{headers:{'X-STX-Router':'true','Accept':'text/html'}}).then(function(r){
1217
- var isFrag=r.headers.get('X-STX-Fragment')==='true';
1250
+ var wantsFragment=shouldUseFragmentResponse();
1251
+ fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
1252
+ var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
1218
1253
  var pLayout=r.headers.get('X-STX-Layout')||'';
1219
1254
  var pGroup=r.headers.get('X-STX-Layout-Group')||'';
1220
1255
  return r.text().then(function(html){return{html:isFrag?'<!--stx-fragment-->'+html:html,layout:pLayout,layoutGroup:pGroup}});
@@ -1315,7 +1350,8 @@ else {
1315
1350
  prefetch:function(url){
1316
1351
  var key=cacheKey(url);
1317
1352
  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(){});
1353
+ var wantsFragment=shouldUseFragmentResponse();
1354
+ fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){var isFrag=wantsFragment&&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(){});
1319
1355
  }
1320
1356
  },
1321
1357
  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]},
package/dist/serve.d.ts CHANGED
@@ -13,7 +13,11 @@ export declare interface ServeOptions {
13
13
  quiet?: boolean
14
14
  stxModule?: typeof import('@stacksjs/stx') | Promise<typeof import('@stacksjs/stx')>
15
15
  routes?: Record<string, (req: Request) => Response | Promise<Response>>
16
- onRequest?: (req: Request) => Response | Promise<Response> | null | undefined
16
+ onRequest?: (req: Request) =>
17
+ | Response
18
+ | null
19
+ | undefined
20
+ | Promise<Response | null | undefined>
17
21
  middleware?: Record<string, MiddlewareHandler>
18
22
  globalMiddleware?: string[]
19
23
  middlewareGroups?: Record<string, string[]>
package/dist/serve.js CHANGED
@@ -8736,6 +8736,16 @@ async function serve(options) {
8736
8736
  cwd: process19.cwd(),
8737
8737
  defaultConfig: defaultStxConfig
8738
8738
  });
8739
+ try {
8740
+ const stxMod = options.stxModule ? options.stxModule : await defaultStxModule;
8741
+ if (stxMod && typeof stxMod.loadStxConfig === "function") {
8742
+ const pluginLoaded = await stxMod.loadStxConfig();
8743
+ if (pluginLoaded?._pluginComponentDirs)
8744
+ stxConfig._pluginComponentDirs = pluginLoaded._pluginComponentDirs;
8745
+ if (pluginLoaded?._pluginPageDirs)
8746
+ stxConfig._pluginPageDirs = pluginLoaded._pluginPageDirs;
8747
+ }
8748
+ } catch {}
8739
8749
  const componentsDir = options.componentsDir ?? stxConfig.componentsDir ?? defaultStxConfig.componentsDir;
8740
8750
  const layoutsDir = options.layoutsDir ?? stxConfig.layoutsDir ?? defaultStxConfig.layoutsDir;
8741
8751
  const partialsDir = options.partialsDir ?? stxConfig.partialsDir ?? defaultStxConfig.partialsDir;
@@ -8959,7 +8969,7 @@ After installing: bun add bun-plugin-stx`);
8959
8969
  pageMiddlewarePatterns.length = 0;
8960
8970
  await Promise.all(files.map((f) => detectPageMiddleware(f)));
8961
8971
  try {
8962
- const { Router } = await import("./chunk-0nwya50v.js");
8972
+ const { Router } = await import("./chunk-ap5bhr00.js");
8963
8973
  const pagesDirs = patterns.map((p) => p.replace(/\/$/, "")).filter(Boolean);
8964
8974
  const router = new Router(process19.cwd(), { pagesDirs });
8965
8975
  console.log(`[stx] Generated ${router.routes.length} routes \u2192 .stx/routes.ts`);
@@ -9162,7 +9172,9 @@ After installing: bun add bun-plugin-stx`);
9162
9172
  ..."defaultTitle" in stxConfig && { defaultTitle: stxConfig.defaultTitle },
9163
9173
  ..."defaultDescription" in stxConfig && { defaultDescription: stxConfig.defaultDescription },
9164
9174
  ..."defaultImage" in stxConfig && { defaultImage: stxConfig.defaultImage },
9165
- ..."seo" in stxConfig && { seo: stxConfig.seo }
9175
+ ..."seo" in stxConfig && { seo: stxConfig.seo },
9176
+ ..."_pluginComponentDirs" in stxConfig && { _pluginComponentDirs: stxConfig._pluginComponentDirs },
9177
+ ..."_pluginPageDirs" in stxConfig && { _pluginPageDirs: stxConfig._pluginPageDirs }
9166
9178
  };
9167
9179
  if (config5.ssr === false || stxConfig.ssr === false) {
9168
9180
  return generateSpaShell({
@@ -9332,7 +9344,9 @@ After installing: bun add bun-plugin-stx`);
9332
9344
  ..."defaultTitle" in stxConfig && { defaultTitle: stxConfig.defaultTitle },
9333
9345
  ..."defaultDescription" in stxConfig && { defaultDescription: stxConfig.defaultDescription },
9334
9346
  ..."defaultImage" in stxConfig && { defaultImage: stxConfig.defaultImage },
9335
- ..."seo" in stxConfig && { seo: stxConfig.seo }
9347
+ ..."seo" in stxConfig && { seo: stxConfig.seo },
9348
+ ..."_pluginComponentDirs" in stxConfig && { _pluginComponentDirs: stxConfig._pluginComponentDirs },
9349
+ ..."_pluginPageDirs" in stxConfig && { _pluginPageDirs: stxConfig._pluginPageDirs }
9336
9350
  };
9337
9351
  let output = templateContent;
9338
9352
  const dependencies = new Set;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-plugin-stx",
3
3
  "type": "module",
4
- "version": "0.2.51",
4
+ "version": "0.2.57",
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",
@@ -61,19 +61,19 @@
61
61
  ],
62
62
  "scripts": {
63
63
  "build": "bun --bun build.ts",
64
- "lint": "bunx --bun pickier lint",
65
- "lint:fix": "bunx --bun pickier lint --fix",
66
- "fresh": "bunx rimraf node_modules/ bun.lock && bun i",
67
- "changelog": "bunx changelogen --output CHANGELOG.md",
64
+ "lint": "bun x --bun pickier lint",
65
+ "lint:fix": "bun x --bun pickier lint --fix",
66
+ "fresh": "bun x rimraf node_modules/ bun.lock && bun i",
67
+ "changelog": "bun x changelogen --output CHANGELOG.md",
68
68
  "prepublishOnly": "bun run build",
69
- "release": "bun run changelog && bunx bumpp package.json --all",
69
+ "release": "bun run changelog && bun x bumpp package.json --all",
70
70
  "test": "bun test --preload ../../test-utils/happy-dom.ts",
71
71
  "test:watch": "bun test --watch --preload ../../test-utils/happy-dom.ts",
72
72
  "test:coverage": "bun test --coverage --preload ../../test-utils/happy-dom.ts",
73
73
  "typecheck": "bun tsc --noEmit"
74
74
  },
75
75
  "dependencies": {
76
- "@stacksjs/stx": "0.2.51",
77
- "stx-router": "0.2.51"
76
+ "@stacksjs/stx": "0.2.57",
77
+ "stx-router": "0.2.57"
78
78
  }
79
79
  }