feat(compiler): support the three api render-binding forms

- Parse api="name", api="name()", and api="name({ ... })" render bindings
  for apis {} (mode "any") blocks, mirroring @click="fn()" syntax.
- Render-bind by calling Task 4's generated server `api` object directly
  (api.<name>(args)) rather than re-implementing the fetch/response
  transport, spliced into the SSR template via the existing loop/expression
  sentinel mechanism so the call runs inside the async render function with
  await support.
- A block that is both render-bound and called from code runs twice by
  design (no dedup); pinned with a test.
- Fix packages/syntax's attribute-value lexer (readQuoted) to honor
  backslash-escaped quotes, needed so an api="..." call expression can
  itself contain a quoted string/object literal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 07:38:32 +05:30
co-authored by Claude Opus 5
parent a08dfa5322
commit 442c058d0c
6 changed files with 189 additions and 42 deletions
+13 -3
View File
@@ -1067,10 +1067,20 @@ export function parseHtmlView(src: string, pos: number): { nodes: ViewNode[]; en
const quote = src[i];
if (quote !== '"' && quote !== "'") return fail("Expected a quoted attribute value");
i++;
const start = i;
while (i < src.length && src[i] !== quote) i++;
// Backslash-escapes the delimiter (and anything else) so an attribute
// value -- e.g. an `api="fn({ a: \"b\" })"` call expression -- can carry
// the same quote character it's wrapped in.
let value = "";
while (i < src.length && src[i] !== quote) {
if (src[i] === "\\" && i + 1 < src.length) {
value += src[i + 1];
i += 2;
continue;
}
value += src[i];
i++;
}
if (i >= src.length) return fail("Unterminated attribute value");
const value = src.slice(start, i);
i++; // closing quote
return value;
};