@stacksjs/stx 0.2.109 → 0.2.111

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.
@@ -199,68 +199,74 @@ function isBalancedForTag(inner, tag) {
199
199
  return !0;
200
200
  }
201
201
  export function convertSignalDirectivesToAttributes(template, context) {
202
- let output = template;
203
- const signalNames = extractClientSignalNames(template), ifDirectiveStart = /@if\s*\(/g;
204
- let match;
205
- const replacements = [];
206
- while ((match = ifDirectiveStart.exec(output)) !== null) {
207
- const startIdx = match.index, exprStart = startIdx + match[0].length;
208
- let depth = 1, i = exprStart;
209
- while (i < output.length && depth > 0) {
210
- if (output[i] === "(")
211
- depth++;
212
- else if (output[i] === ")")
213
- depth--;
214
- i++;
215
- }
216
- if (depth !== 0)
217
- continue;
218
- const condition = output.substring(exprStart, i - 1).trim(), afterCondition = i;
219
- let ifDepth = 1, endIdx = afterCondition;
220
- const endifRegex = /@(if\s*\(|endif)/g;
221
- endifRegex.lastIndex = afterCondition;
222
- let endMatch;
223
- while ((endMatch = endifRegex.exec(output)) !== null)
224
- if (endMatch[1].startsWith("if"))
225
- ifDepth++;
226
- else if (endMatch[1] === "endif") {
227
- ifDepth--;
228
- if (ifDepth === 0) {
229
- endIdx = endMatch.index + endMatch[0].length;
230
- break;
231
- }
202
+ const signalNames = extractClientSignalNames(template);
203
+ function convert(input) {
204
+ let output = input;
205
+ const ifDirectiveStart = /@if\s*\(/g;
206
+ let match;
207
+ const replacements = [];
208
+ while ((match = ifDirectiveStart.exec(output)) !== null) {
209
+ const startIdx = match.index, exprStart = startIdx + match[0].length;
210
+ let depth = 1, i = exprStart;
211
+ while (i < output.length && depth > 0) {
212
+ if (output[i] === "(")
213
+ depth++;
214
+ else if (output[i] === ")")
215
+ depth--;
216
+ i++;
232
217
  }
233
- if (ifDepth !== 0)
234
- continue;
235
- const content = output.substring(afterCondition, endIdx - 6).trim();
236
- let replacement;
237
- if (hasTopLevelElseBranch(content)) {
238
- const parsed = findIfBlocks(output.substring(startIdx, endIdx));
239
- if (parsed.length === 0)
218
+ if (depth !== 0)
240
219
  continue;
241
- const valueBranches = parsed[0].branches.filter((b) => b.type !== "else");
242
- if (!(valueBranches.length > 0 && valueBranches.every((b) => conditionIsClientReactive(b.condition, signalNames, context))))
220
+ const condition = output.substring(exprStart, i - 1).trim(), afterCondition = i;
221
+ let ifDepth = 1, endIdx = afterCondition;
222
+ const endifRegex = /@(if\s*\(|endif)/g;
223
+ endifRegex.lastIndex = afterCondition;
224
+ let endMatch;
225
+ while ((endMatch = endifRegex.exec(output)) !== null)
226
+ if (endMatch[1].startsWith("if"))
227
+ ifDepth++;
228
+ else if (endMatch[1] === "endif") {
229
+ ifDepth--;
230
+ if (ifDepth === 0) {
231
+ endIdx = endMatch.index + endMatch[0].length;
232
+ break;
233
+ }
234
+ }
235
+ if (ifDepth !== 0)
243
236
  continue;
244
- replacement = parsed[0].branches.map((branch) => {
245
- if (branch.type === "elseif")
246
- return branchToAttrElement(branch.content, "@else-if", branch.condition ?? "");
247
- if (branch.type === "else")
248
- return branchToAttrElement(branch.content, "@else");
249
- return branchToAttrElement(branch.content, "@if", branch.condition ?? "");
250
- }).join(`
237
+ const content = output.substring(afterCondition, endIdx - 6).trim();
238
+ let replacement;
239
+ if (hasTopLevelElseBranch(content)) {
240
+ const parsed = findIfBlocks(output.substring(startIdx, endIdx));
241
+ if (parsed.length === 0)
242
+ continue;
243
+ const valueBranches = parsed[0].branches.filter((b) => b.type !== "else");
244
+ if (!(valueBranches.length > 0 && valueBranches.every((b) => conditionIsClientReactive(b.condition, signalNames, context))))
245
+ continue;
246
+ replacement = parsed[0].branches.map((branch) => {
247
+ const branchContent = convert(branch.content);
248
+ if (branch.type === "elseif")
249
+ return branchToAttrElement(branchContent, "@else-if", branch.condition ?? "");
250
+ if (branch.type === "else")
251
+ return branchToAttrElement(branchContent, "@else");
252
+ return branchToAttrElement(branchContent, "@if", branch.condition ?? "");
253
+ }).join(`
251
254
  `);
252
- } else {
253
- if (!conditionIsClientReactive(condition, signalNames, context))
254
- continue;
255
- replacement = branchToAttrElement(content, "@if", condition);
255
+ } else {
256
+ if (!conditionIsClientReactive(condition, signalNames, context))
257
+ continue;
258
+ replacement = branchToAttrElement(convert(content), "@if", condition);
259
+ }
260
+ replacements.push({ start: startIdx, end: endIdx, replacement });
261
+ ifDirectiveStart.lastIndex = endIdx;
256
262
  }
257
- replacements.push({ start: startIdx, end: endIdx, replacement });
258
- }
259
- for (let i = replacements.length - 1;i >= 0; i--) {
260
- const { start, end, replacement } = replacements[i];
261
- output = output.substring(0, start) + replacement + output.substring(end);
263
+ for (let i = replacements.length - 1;i >= 0; i--) {
264
+ const { start, end, replacement } = replacements[i];
265
+ output = output.substring(0, start) + replacement + output.substring(end);
266
+ }
267
+ return output;
262
268
  }
263
- return output;
269
+ return convert(template);
264
270
  }
265
271
  function evalLiteralCondition(expr) {
266
272
  const trimmed = expr.trim();
package/dist/signals.js CHANGED
@@ -1579,7 +1579,7 @@ catch (e) {
1579
1579
  if (el.nodeType === Node.TEXT_NODE) {
1580
1580
  const text = el.textContent;
1581
1581
  if (text && text.includes('{{')) {
1582
- const parts = text.split(/(\\{\\{[^}]+\\}\\})/g);
1582
+ const parts = text.split(/(\\{\\{[\\s\\S]+?\\}\\})/g);
1583
1583
  if (parts.length > 1) {
1584
1584
  const fragment = document.createDocumentFragment();
1585
1585
  const parentEl = el.parentNode;
@@ -1588,7 +1588,7 @@ catch (e) {
1588
1588
  // through nested @if/@for processing where componentScope may be restored
1589
1589
  const capturedScope = { ...scope, ...(findElementScope(parentEl) || {}), ...globalHelpers };
1590
1590
  parts.forEach(part => {
1591
- const match = part.match(/^\\{\\{\\s*(.+?)\\s*\\}\\}$/);
1591
+ const match = part.match(/^\\{\\{\\s*([\\s\\S]+?)\\s*\\}\\}$/);
1592
1592
  if (match) {
1593
1593
  const expr = match[1];
1594
1594
  // Skip placeholder expressions like __TITLE__ (build-time placeholders)
@@ -2775,6 +2775,18 @@ catch (e) {
2775
2775
  b.placeholder = document.createComment('stx-if-chain');
2776
2776
  parent.insertBefore(b.placeholder, b.el);
2777
2777
  b.el.removeAttribute(b.attr);
2778
+ // A <template> branch (the wrapper convertSignalDirectivesToAttributes
2779
+ // emits for a multi-child / text branch) is INERT \u2014 inserting the element
2780
+ // renders nothing, its content lives in a document fragment. Clone that
2781
+ // content ONCE into a stable node set we toggle in and out, exactly as
2782
+ // bindIf/bindFor already do for template elements. An element branch is
2783
+ // its own single node. Without this, a multi-element @else / @else-if
2784
+ // branch rendered blank the moment the markup was well-formed enough to
2785
+ // survive as a real <template> (the #1784 markup fix). See #1784.
2786
+ b.isTemplate = b.el.tagName === 'TEMPLATE';
2787
+ b.nodes = b.isTemplate
2788
+ ? Array.prototype.slice.call(b.el.content.cloneNode(true).childNodes)
2789
+ : [b.el];
2778
2790
  b.el.remove();
2779
2791
  b.childrenProcessed = false;
2780
2792
  });
@@ -2858,13 +2870,16 @@ catch (e2) {
2858
2870
  // components under reactive conditionals (#1737). Permanent disposal is
2859
2871
  // still handled by bindFor (item removal) and cleanupContainer (SPA nav).
2860
2872
  if (currentIdx !== -1) {
2861
- chain[currentIdx].el.remove();
2873
+ chain[currentIdx].nodes.forEach(function (n) { if (n.parentNode) n.parentNode.removeChild(n); });
2862
2874
  }
2863
2875
 
2864
2876
  // Insert + process the newly-picked branch.
2865
2877
  if (pickedIdx !== -1) {
2866
2878
  var pick = chain[pickedIdx];
2867
- pick.placeholder.parentNode.insertBefore(pick.el, pick.placeholder.nextSibling);
2879
+ // Insert this branch's node(s) before the placeholder's next sibling.
2880
+ // The anchor is captured once so the nodes stack in source order.
2881
+ var anchor = pick.placeholder.nextSibling;
2882
+ pick.nodes.forEach(function (n) { pick.placeholder.parentNode.insertBefore(n, anchor); });
2868
2883
  pick.el.__stx_shown_at = performance.now();
2869
2884
  if (!pick.childrenProcessed) {
2870
2885
  pick.childrenProcessed = true;
@@ -2874,9 +2889,12 @@ catch (e2) {
2874
2889
  (function (branch) {
2875
2890
  setTimeout(function () {
2876
2891
  var childScope = { ...capturedComponentScope, ...(branch.capturedElementScope || {}), ...globalHelpers };
2877
- processElement(branch.el, childScope);
2878
- branch.el.removeAttribute('x-cloak');
2879
- branch.el.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
2892
+ branch.nodes.forEach(function (n) {
2893
+ if (n.nodeType !== 1) return; // whitespace/text between template children
2894
+ processElement(n, childScope);
2895
+ n.removeAttribute('x-cloak');
2896
+ n.querySelectorAll('[x-cloak]').forEach(function (c) { c.removeAttribute('x-cloak'); });
2897
+ });
2880
2898
  }, 0);
2881
2899
  })(pick);
2882
2900
  }