@stacksjs/stx 0.2.110 → 0.2.112

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.
@@ -269,12 +269,17 @@ function expressionUsesOnlyContextVars(expr, context) {
269
269
  if (ch === "$" && exprWithoutStrings[ti + 1] === "{") {
270
270
  tplDepth++;
271
271
  ti++;
272
+ tplResult += " ";
272
273
  continue;
273
274
  }
274
275
  if (ch === "}" && tplDepth > 0) {
275
276
  tplDepth--;
276
277
  continue;
277
278
  }
279
+ if (tplDepth > 0) {
280
+ tplResult += ch;
281
+ continue;
282
+ }
278
283
  continue;
279
284
  }
280
285
  tplResult += ch;
@@ -378,7 +383,7 @@ export function processExpressions(template, context, filePath, options) {
378
383
  return "";
379
384
  try {
380
385
  const value = evaluateExpression(expr, context);
381
- if (value === void 0 && hasSignals && !expressionUsesOnlyContextVars(trimmedExpr, context))
386
+ if (hasSignals && !expressionUsesOnlyContextVars(trimmedExpr, context))
382
387
  return match;
383
388
  if (value === void 0 && context.__stx_buildMode === "compile" && !hasSignals)
384
389
  return createPlaceholder("expr", trimmedExpr);
package/dist/includes.js CHANGED
@@ -189,8 +189,8 @@ ${scriptContent}
189
189
  ${tail}})();
190
190
  `;
191
191
  }
192
- function transformSignalScript(scriptContent, scopeId) {
193
- const tail = `
192
+ function buildScopeRegistrationTail(scriptContent, scopeId) {
193
+ return `
194
194
  // Register scope variables for STX runtime
195
195
  if (!window.stx._scopes) window.stx._scopes = {};
196
196
  var __scopeRegistration = { __destroyCallbacks: __destroyHooks };
@@ -202,7 +202,10 @@ ${extractExports(scriptContent).split(",").map((s) => s.trim()).filter(Boolean).
202
202
  // shadowing import. __stxScopeRenames is emitted by bundleClientScript.
203
203
  try { if (typeof __stxScopeRenames !== 'undefined' && __stxScopeRenames) { for (var __stxRK in __stxScopeRenames) __scopeRegistration[__stxRK] = __stxScopeRenames[__stxRK]; } } catch (e) { /* no renames */ }
204
204
  window.stx._scopes['${scopeId}'] = __scopeRegistration;
205
- `, head = `
205
+ `;
206
+ }
207
+ function transformSignalScript(scriptContent, scopeId) {
208
+ const tail = buildScopeRegistrationTail(scriptContent, scopeId), head = `
206
209
  (function() {
207
210
  var __stxScopeEl = (typeof document !== 'undefined') ? document.querySelector('[data-stx-scope="${scopeId}"]') : null;
208
211
  var __stxPrevEl = (typeof window !== 'undefined') ? window.__STX_CURRENT_ELEMENT__ : undefined;
@@ -542,7 +545,7 @@ export async function processIncludes(template, context, filePath, options, depe
542
545
  if (options.debug)
543
546
  console.warn(`[stx] partial ES import extraction skipped for ${includeFilePath}:`, err instanceof Error ? err.message : err);
544
547
  }
545
- const templateMatch = workingContent.match(/<template\b(?![^>]*(?:\b(?:id|x-for|x-if|@for|@if|:for|:if)\s*=|\s#[\w-]|\bv-slot|\bslot\s*=))[^>]*>([\s\S]*?)<\/template>/i);
548
+ const templateMatch = workingContent.match(/<template\b(?![^>]*(?:\b(?:id|x-for|x-if|x-else|@for|@if|@else|:for|:if|:else)\s*=|\s#[\w-]|\bv-slot|\bslot\s*=))[^>]*>([\s\S]*?)<\/template>/i);
546
549
  if (templateMatch)
547
550
  workingContent = templateMatch[1].trim();
548
551
  const styleMatch = partialContent.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").match(/<style\b([^>]*)>([\s\S]*?)<\/style>/i);
@@ -604,10 +607,7 @@ export async function processIncludes(template, context, filePath, options, depe
604
607
  if (!isServerScript && !isSignalScript) {
605
608
  const extraAttrs = scriptAttrs.replace(/\bclient\b/i, "").trim();
606
609
  signalScopeId = `stx_scope_${path.basename(includeFilePath, ".stx").replace(/[^a-zA-Z0-9]/g, "_")}_${++scopeIdCounter}`;
607
- const resolvedContent = transformStoreImports(scriptContent), tail = `
608
- if (!window.stx._scopes) window.stx._scopes = {};
609
- window.stx._scopes['${signalScopeId}'] = { __destroyCallbacks: __destroyHooks };
610
- `, wrapped = wrapClientScript(resolvedContent, tail);
610
+ const resolvedContent = transformStoreImports(scriptContent), tail = buildScopeRegistrationTail(resolvedContent, signalScopeId), wrapped = wrapClientScript(resolvedContent, tail);
611
611
  preservedScript += `${vendorStyleTags}<script data-stx-scoped${extraAttrs ? ` ${extraAttrs}` : ""}>${wrapped}</script>
612
612
  `;
613
613
  }
@@ -11,6 +11,21 @@ export declare function extractParenthesizedExpression(source: string, startPos:
11
11
  endPos: number
12
12
  } | null;
13
13
  export declare function extractParenthesizedExpressionDetailed(source: string, startPos: number): ExtractParenResult;
14
+ /**
15
+ * Whether `pos` in `source` falls INSIDE an HTML tag (`<...>`) — i.e. the nearest
16
+ * angle bracket looking backwards is an unclosed `<`.
17
+ *
18
+ * Directive tokens (`@if`/`@elseif`/`@else`/`@endif`) only ever occur in text
19
+ * content, never inside a tag. The reactive-signal pass, however, rewrites a
20
+ * NESTED reactive conditional into HTML-attribute markers BEFORE this server pass
21
+ * runs — e.g. `@if (isPro())A@else B@endif` becomes
22
+ * `<template @if="isPro()">A</template><template @else>B</template>`. Without this
23
+ * guard the branch scanner matches that `@else` ATTRIBUTE as the enclosing server
24
+ * `@if`'s top-level `@else`, mismatching branch/`@endif` boundaries and corrupting
25
+ * the rest of the template. Skipping tag-interior tokens keeps the two passes from
26
+ * disagreeing on what is a directive.
27
+ */
28
+ export declare function isInsideTag(source: string, pos: number): boolean;
14
29
  /**
15
30
  * Find the matching end tag for a directive, handling nested instances
16
31
  *
@@ -14,6 +14,12 @@ export function extractParenthesizedExpressionDetailed(source, startPos) {
14
14
  return { ok: !1, reason: "unclosed-paren", pos };
15
15
  return { ok: !0, expression: source.slice(pos + 1, closePos), endPos: closePos + 1 };
16
16
  }
17
+ export function isInsideTag(source, pos) {
18
+ const lastOpen = source.lastIndexOf("<", pos);
19
+ if (lastOpen === -1)
20
+ return !1;
21
+ return lastOpen > source.lastIndexOf(">", pos);
22
+ }
17
23
  export function findMatchingEndTag(source, startName, endName, startPos) {
18
24
  let depth = 1, pos = startPos;
19
25
  const openPattern = new RegExp(`@${startName}\\s*\\(`, "g"), closePattern = new RegExp(`@${endName}(?![a-z])`, "g");
@@ -52,6 +58,10 @@ export function parseConditionalBlock(source, startPos) {
52
58
  break;
53
59
  currentPos = atIdx;
54
60
  const remaining = fullContent.substring(atIdx);
61
+ if (isInsideTag(fullContent, atIdx)) {
62
+ currentPos++;
63
+ continue;
64
+ }
55
65
  if (/^@if\s*\(/.test(remaining)) {
56
66
  nestedDepth++;
57
67
  currentPos++;