stx-router 0.2.381 → 0.2.383

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 +42 -1
  2. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -472,6 +472,47 @@ function routerSource() {
472
472
  function isComposablesScript(s){
473
473
  return !!s&&((s.hasAttribute&&s.hasAttribute('data-stx-composables'))||COMPOSABLES_TEXT.test(s.textContent||''));
474
474
  }
475
+ /*
476
+ * What the block wrap below would otherwise hide (stacksjs/stx#2041).
477
+ *
478
+ * A re-executed script is wrapped in a bare block so that top-level const
479
+ * and let cannot collide across navigations. That wrap is invisible for a
480
+ * plain function declaration -- Annex B.3.3 still hoists its binding to the
481
+ * enclosing scope in sloppy mode -- and NOT invisible for anything else.
482
+ * async function, function*, class, const and let are all block-scoped, so
483
+ * the same source published a global when the browser parsed the script and
484
+ * published nothing when the router re-ran it.
485
+ *
486
+ * The effect is a divergence between development and a built app, because
487
+ * only the built one reaches a route as a fragment: a page served whole runs
488
+ * its script natively. An inline onclick that names such a declaration then
489
+ * worked in dev and threw ReferenceError in the packaged app, where there is
490
+ * no console, so it read as a button that does nothing.
491
+ *
492
+ * So: re-publish what the browser would have published. Only values that are
493
+ * functions, which is what an inline handler calls -- assigning every
494
+ * top-level const would otherwise write over window.name and friends, which
495
+ * a lexical global shadows rather than overwrites. Each name is guarded on
496
+ * its own so an unmatched one cannot take the rest down, and typeof is safe
497
+ * on a name that was never declared.
498
+ *
499
+ * Column-anchored: a declaration nested inside a function is indented, and
500
+ * is not in scope at the end of the block anyway.
501
+ */
502
+ var TOP_LEVEL_DECL=/^(?:export\\s+)?(?:async\\s+)?(?:function\\s*\\*?|class|const|let|var)\\s+([A-Za-z_$][\\w$]*)/gm;
503
+ function republishTopLevel(src){
504
+ var names=[],m;
505
+ TOP_LEVEL_DECL.lastIndex=0;
506
+ while((m=TOP_LEVEL_DECL.exec(src))!==null){
507
+ if(names.indexOf(m[1])===-1)names.push(m[1]);
508
+ }
509
+ if(!names.length)return '';
510
+ var out='\\n;';
511
+ for(var i=0;i<names.length;i++){
512
+ out+='try{if(typeof '+names[i]+'==="function")window.'+names[i]+'='+names[i]+'}catch(e){}';
513
+ }
514
+ return out;
515
+ }
475
516
  function runsAlways(declared, code){
476
517
  if(declared==='always')return true;
477
518
  if(declared==='once')return false;
@@ -1732,7 +1773,7 @@ else {
1732
1773
  // ESM, and top-level 'import' is illegal inside a block, which
1733
1774
  // would throw SyntaxError before the script ever runs.
1734
1775
  var alreadyScoped=runsAlways(typeof entry==='string'?'':entry.run,text);
1735
- ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
1776
+ ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+republishTopLevel(text)+'}';
1736
1777
  ns.setAttribute('data-stx-page','');
1737
1778
  var placeholder=entry.slot?qs('script[data-stx-route-script="'+entry.slot+'"]'):null;
1738
1779
  if(placeholder&&placeholder.parentNode){
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stx-router",
3
3
  "type": "module",
4
- "version": "0.2.381",
4
+ "version": "0.2.383",
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",