@stacksjs/stx 0.2.115 → 0.2.116

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.
@@ -5,8 +5,10 @@
5
5
  * @param declaration `var` for scripts that reassign a binding (the `@include`
6
6
  * wrapper rebinds `onDestroy` to tee into its teardown channel — an assignment,
7
7
  * which `var` permits), `const` otherwise.
8
- * @param exclude names the call site declares itself; see
9
- * {@link COMPONENT_SCOPE_LOCAL_GLOBALS}.
8
+ * @param exclude names the generated wrapper or user script declares itself.
9
+ * A local declaration is intentionally allowed to shadow a runtime convenience
10
+ * global, just as an explicit import would. Omitting it from this destructure
11
+ * prevents duplicate-binding syntax errors.
10
12
  */
11
13
  export declare function buildRuntimeGlobalsDestructure(declaration?: 'const' | 'var', exclude?: readonly string[]): string;
12
14
  /**
@@ -696,10 +696,10 @@ ${resolved}`);
696
696
 
697
697
  `);
698
698
  mergedContent = dedupeTopLevelDeclarations(mergedContent);
699
- const mergedExports = extractExports(mergedContent), bridge = generateServerDataBridge(mergedContent, serverData), setupCode = `
699
+ const mergedExports = extractExports(mergedContent), mergedExportNames = mergedExports.split(",").map((name) => name.trim()).filter(Boolean), bridge = generateServerDataBridge(mergedContent, serverData), setupCode = `
700
700
  <script data-stx-scoped>
701
701
  function ${setupFnName}() {
702
- ${buildRuntimeGlobalsDestructure("const")}
702
+ ${buildRuntimeGlobalsDestructure("const", mergedExportNames)}
703
703
  ${bridge}${mergedContent}
704
704
  return { ${mergedExports} };
705
705
  }
package/dist/signals.js CHANGED
@@ -1969,6 +1969,7 @@ catch (e2) {
1969
1969
  Array.from(el.attributes).forEach(attr => {
1970
1970
  const name = attr.name;
1971
1971
  const value = attr.value;
1972
+ if (!name) return;
1972
1973
 
1973
1974
  if (el.hasAttribute && (el.hasAttribute('x-class') || el.hasAttribute('@click'))) {
1974
1975
  console.log('[stx] attr iter:', el.tagName, 'name:', name);
@@ -2001,9 +2002,34 @@ catch (e2) {
2001
2002
  catch (e) { controlValue = String(v); }
2002
2003
  }
2003
2004
  controlValue = v === false || v === null || v === undefined ? '' : String(controlValue);
2004
- el.value = controlValue;
2005
- if (controlValue === '' && el.tagName !== 'OPTION') el.removeAttribute(attrName);
2006
- else el.setAttribute(attrName, controlValue);
2005
+ el.__stx_bound_value = controlValue;
2006
+ var applyControlValue = function() {
2007
+ var desiredValue = el.__stx_bound_value == null ? '' : String(el.__stx_bound_value);
2008
+ el.value = desiredValue;
2009
+ if (desiredValue === '' && el.tagName !== 'OPTION') el.removeAttribute(attrName);
2010
+ else if (el.getAttribute(attrName) !== desiredValue) el.setAttribute(attrName, desiredValue);
2011
+ };
2012
+ applyControlValue();
2013
+
2014
+ // A select's value binding is processed before its child
2015
+ // directives. When a nested :for creates the matching options
2016
+ // afterward, the browser otherwise leaves the first option
2017
+ // selected. Keep the desired value and reapply it whenever the
2018
+ // option tree changes, including later reactive list updates.
2019
+ if (el.tagName === 'SELECT' && !el.__stx_value_observer && typeof window.MutationObserver !== 'undefined') {
2020
+ el.__stx_value_observer = new window.MutationObserver(applyControlValue);
2021
+ el.__stx_value_observer.observe(el, {
2022
+ childList: true,
2023
+ subtree: true,
2024
+ attributes: true,
2025
+ attributeFilter: ['value'],
2026
+ });
2027
+ el.__stx_destroy = el.__stx_destroy || [];
2028
+ el.__stx_destroy.push(function() {
2029
+ if (el.__stx_value_observer) el.__stx_value_observer.disconnect();
2030
+ el.__stx_value_observer = null;
2031
+ });
2032
+ }
2007
2033
  return;
2008
2034
  }
2009
2035
  if (v === false || v === null || v === undefined) {
@@ -3238,7 +3264,11 @@ catch (e2) {
3238
3264
  const content = el.content;
3239
3265
  currentNodes = Array.from(content.childNodes).map(n => n.cloneNode(true));
3240
3266
  // Insert cloned content initially
3241
- currentNodes.forEach(node => parent.insertBefore(node, placeholder.nextSibling));
3267
+ // Capture the anchor once. Re-reading placeholder.nextSibling after
3268
+ // every insertion points at the node just inserted and reverses a
3269
+ // multi-root template branch.
3270
+ const initialAnchor = placeholder.nextSibling;
3271
+ currentNodes.forEach(node => parent.insertBefore(node, initialAnchor));
3242
3272
  el.remove(); // Remove the template element itself
3243
3273
  }
3244
3274
 
@@ -3319,7 +3349,8 @@ catch (e2) {
3319
3349
  if (value && !isInserted) {
3320
3350
  // Re-insert cloned content
3321
3351
  currentNodes = Array.from(el.content.childNodes).map(n => n.cloneNode(true));
3322
- currentNodes.forEach(node => parent.insertBefore(node, placeholder.nextSibling));
3352
+ const insertionAnchor = placeholder.nextSibling;
3353
+ currentNodes.forEach(node => parent.insertBefore(node, insertionAnchor));
3323
3354
  // Process the new nodes \u2014 use LIVE componentScope so all functions are available
3324
3355
  const childScope = { ...componentScope, ...(capturedElementScope || {}) };
3325
3356
  currentNodes.forEach(node => {
@@ -4462,7 +4493,8 @@ catch (e) {}
4462
4493
  var scopeId = el.getAttribute('data-stx-scope');
4463
4494
  var scopeVars = window.stx._scopes && window.stx._scopes[scopeId];
4464
4495
  if (!scopeVars) return;
4465
- el.__stx_parent_scope = { ...componentScope };
4496
+ el.__stx_parent_scope = el.__stx_parent_scope
4497
+ || resolveComponentCallerScope(el, componentScope);
4466
4498
  bindParentComponentProps(el, el.__stx_parent_scope);
4467
4499
  Object.assign(componentScope, scopeVars);
4468
4500
  el.__stx_disposers = trackEffects(function() { processElement(el, componentScope); });
@@ -5324,7 +5356,8 @@ else {
5324
5356
  // Forwarded props and events belong to the caller. Capture its scope
5325
5357
  // before this component's locals are merged, so identical child names
5326
5358
  // cannot shadow expressions such as :total-items="totalItems".
5327
- el.__stx_parent_scope = resolveComponentCallerScope(el, pageScopeSnapshot);
5359
+ el.__stx_parent_scope = el.__stx_parent_scope
5360
+ || resolveComponentCallerScope(el, pageScopeSnapshot);
5328
5361
  bindParentComponentProps(el, el.__stx_parent_scope);
5329
5362
 
5330
5363
  // Merge component scope vars into componentScope (don't restore - keep for head elements)
@@ -5705,7 +5738,8 @@ catch (e) { console.warn('[stx] destroy callback error:', e); }
5705
5738
  return;
5706
5739
  }
5707
5740
  if (!scopeVars) return;
5708
- el.__stx_parent_scope = resolveComponentCallerScope(el, spaPageScopeSnapshot);
5741
+ el.__stx_parent_scope = el.__stx_parent_scope
5742
+ || resolveComponentCallerScope(el, spaPageScopeSnapshot);
5709
5743
  bindParentComponentProps(el, el.__stx_parent_scope);
5710
5744
  Object.assign(componentScope, scopeVars);
5711
5745
  if (el.__stx_disposers) return;