@torpor/view 1.0.2 → 1.1.0

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 (33) hide show
  1. package/dist/{fromWebSocket-DeB35Q4w.d.cts → AsyncOptions-BtMe2PAd.d.cts} +21 -2
  2. package/dist/AsyncOptions-BtMe2PAd.d.cts.map +1 -0
  3. package/dist/{fromWebSocket-B52kN3-W.d.mts → AsyncOptions-ClS1_ERX.d.mts} +21 -2
  4. package/dist/AsyncOptions-ClS1_ERX.d.mts.map +1 -0
  5. package/dist/{Effect-BTKurOpr.d.mts → Effect-DazmAdyC.d.mts} +3 -3
  6. package/dist/{Effect-BTKurOpr.d.mts.map → Effect-DazmAdyC.d.mts.map} +1 -1
  7. package/dist/{Effect-COl3aRJV.d.cts → Effect-Dewn7MLc.d.cts} +3 -3
  8. package/dist/{Effect-COl3aRJV.d.cts.map → Effect-Dewn7MLc.d.cts.map} +1 -1
  9. package/dist/compile.cjs +284 -224
  10. package/dist/compile.mjs +284 -224
  11. package/dist/compile.mjs.map +1 -1
  12. package/dist/dev.d.cts +1 -1
  13. package/dist/dev.d.mts +1 -1
  14. package/dist/{fromWebSocket-Dxc_uwm1.mjs → fromWebSocket-CFGoMkV2.mjs} +9 -7
  15. package/dist/{fromWebSocket-Dxc_uwm1.mjs.map → fromWebSocket-CFGoMkV2.mjs.map} +1 -1
  16. package/dist/{fromWebSocket-1QOjcrHE.cjs → fromWebSocket-DrN-IduP.cjs} +8 -6
  17. package/dist/index.cjs +121 -41
  18. package/dist/index.d.cts +33 -7
  19. package/dist/index.d.cts.map +1 -1
  20. package/dist/index.d.mts +33 -7
  21. package/dist/index.d.mts.map +1 -1
  22. package/dist/index.mjs +121 -42
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/ssr.cjs +293 -5
  25. package/dist/ssr.d.cts +108 -9
  26. package/dist/ssr.d.cts.map +1 -1
  27. package/dist/ssr.d.mts +108 -9
  28. package/dist/ssr.d.mts.map +1 -1
  29. package/dist/ssr.mjs +292 -6
  30. package/dist/ssr.mjs.map +1 -1
  31. package/package.json +3 -3
  32. package/dist/fromWebSocket-B52kN3-W.d.mts.map +0 -1
  33. package/dist/fromWebSocket-DeB35Q4w.d.cts.map +0 -1
package/dist/compile.mjs CHANGED
@@ -537,9 +537,7 @@ function buildNodeFragmentText(node, status, fragments, currentFragment) {
537
537
  if (currentFragment) currentFragment.text += isReactive(content) ? "#" : content;
538
538
  break;
539
539
  }
540
- case "special":
541
- buildSpecialFragmentText(node, status, fragments, currentFragment);
542
- break;
540
+ case "special": buildSpecialFragmentText(node, status, fragments, currentFragment);
543
541
  }
544
542
  }
545
543
  function buildRootFragmentText(node, status, fragments) {
@@ -584,7 +582,6 @@ function buildControlFragmentText(node, status, fragments, currentFragment) {
584
582
  };
585
583
  fragments.push(node.fragment);
586
584
  for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
587
- break;
588
585
  }
589
586
  }
590
587
  function buildComponentFragmentText(node, status, fragments, currentFragment) {
@@ -934,9 +931,7 @@ function declareControlFragmentVars(fragment, node, path, status, b, parentName,
934
931
  case "@html group":
935
932
  declareParentAndAnchorFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, node.operation.substring(1).replace(" group", ""), declare);
936
933
  break;
937
- default:
938
- for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
939
- break;
934
+ default: for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
940
935
  }
941
936
  }
942
937
  function declareComponentFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare) {
@@ -1005,8 +1000,10 @@ function declareElementFragmentVars(fragment, node, path, status, b, parentName,
1005
1000
  else b.append(`t_elm("${node.tagName}", {${attributes}}, [`);
1006
1001
  }
1007
1002
  for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, elementPath, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
1008
- if (!declare) if (declareVariable || childParentName) b.append("])),");
1009
- else b.append("]),");
1003
+ if (!declare) {
1004
+ if (declareVariable || childParentName) b.append("])),");
1005
+ else b.append("]),");
1006
+ }
1010
1007
  }
1011
1008
  function elementNodeNeedsDeclaration(node, topLevel, lastChild) {
1012
1009
  const hasReactiveAttribute = node.attributes.some((a) => a.value && a.reactive);
@@ -1127,10 +1124,12 @@ function getFragmentVarPath(fragment, status, name, path, varPaths) {
1127
1124
  let level = 0;
1128
1125
  let end = 0;
1129
1126
  for (let i = pos + match.length; i < varPath.length; i++) if (varPath[i] === "(") level++;
1130
- else if (varPath[i] === ")") if (level === 0) {
1131
- end = i;
1132
- break;
1133
- } else level--;
1127
+ else if (varPath[i] === ")") {
1128
+ if (level === 0) {
1129
+ end = i;
1130
+ break;
1131
+ } else level--;
1132
+ }
1134
1133
  const tail = varPath.substring(end + count);
1135
1134
  varPath = `${head}t_skip(${varPath.substring(pos + match.length, end)}, ${count})${tail}`;
1136
1135
  }
@@ -1146,10 +1145,12 @@ function getFragmentVarPathPart(path, varPath, status, root = false, rootFn = "t
1146
1145
  varPath = `t_child(${varPath})`;
1147
1146
  }
1148
1147
  for (let [i, child] of path.children.entries()) {
1149
- if (i > 0) if (child.type === "text" || child.type === "space") varPath = `t_next_text(${varPath}, true)`;
1150
- else {
1151
- status.imports.add("t_next");
1152
- varPath = `t_next(${varPath})`;
1148
+ if (i > 0) {
1149
+ if (child.type === "text" || child.type === "space") varPath = `t_next_text(${varPath}, true)`;
1150
+ else {
1151
+ status.imports.add("t_next");
1152
+ varPath = `t_next(${varPath})`;
1153
+ }
1153
1154
  }
1154
1155
  if (i === path.children.length - 1 && child.children.length) varPath = getFragmentVarPathPart(child, varPath, status);
1155
1156
  }
@@ -1284,39 +1285,40 @@ function buildComponentNode(node, status, b, root = false) {
1284
1285
  let props = [];
1285
1286
  let runs = [];
1286
1287
  let bindingRuns = [];
1287
- for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@component") {} else if (value != null && fullyReactive) if (name.startsWith("&")) {
1288
- name = name.substring(1);
1289
- props.push({
1290
- name,
1291
- value,
1292
- spans: [span]
1293
- });
1294
- runs.push(`${propsName}["${name}"] = ${value};`);
1295
- bindingRuns.push(bindingTarget(value, `${propsName}["${name}"]`, name, status.imports));
1296
- } else if (name === "class") {
1297
- if (node.scopeStyles) value = `[${value}, "torp-${status.styleHash}"]`;
1298
- props.push({
1299
- name,
1300
- value,
1301
- spans: [span]
1302
- });
1303
- runs.push(`${propsName}["${name}"] = ${value};`);
1304
- } else if (name === "style") {
1305
- props.push({
1306
- name,
1307
- value,
1308
- spans: [span]
1309
- });
1310
- runs.push(`${propsName}["${name}"] = ${value};`);
1311
- } else {
1312
- props.push({
1313
- name,
1314
- value,
1315
- spans: [span]
1316
- });
1317
- runs.push(`${propsName}["${name}"] = ${value};`);
1318
- }
1319
- else if (value != null && reactive) {
1288
+ for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@component") {} else if (value != null && fullyReactive) {
1289
+ if (name.startsWith("&")) {
1290
+ name = name.substring(1);
1291
+ props.push({
1292
+ name,
1293
+ value,
1294
+ spans: [span]
1295
+ });
1296
+ runs.push(`${propsName}["${name}"] = ${value};`);
1297
+ bindingRuns.push(bindingTarget(value, `${propsName}["${name}"]`, name, status.imports));
1298
+ } else if (name === "class") {
1299
+ if (node.scopeStyles) value = `[${value}, "torp-${status.styleHash}"]`;
1300
+ props.push({
1301
+ name,
1302
+ value,
1303
+ spans: [span]
1304
+ });
1305
+ runs.push(`${propsName}["${name}"] = ${value};`);
1306
+ } else if (name === "style") {
1307
+ props.push({
1308
+ name,
1309
+ value,
1310
+ spans: [span]
1311
+ });
1312
+ runs.push(`${propsName}["${name}"] = ${value};`);
1313
+ } else {
1314
+ props.push({
1315
+ name,
1316
+ value,
1317
+ spans: [span]
1318
+ });
1319
+ runs.push(`${propsName}["${name}"] = ${value};`);
1320
+ }
1321
+ } else if (value != null && reactive) {
1320
1322
  const { newValue, spans, offsets, lengths } = getAttributeOffsets(value, span);
1321
1323
  value = newValue;
1322
1324
  if (name === "class") {
@@ -1574,8 +1576,10 @@ function collectExpressionStrings(node, out) {
1574
1576
  }
1575
1577
  if (isElementLike(node)) {
1576
1578
  for (const attr of node.attributes) {
1577
- if (attr.value !== void 0) if (attr.name.startsWith("&")) out.push(`${attr.value} =`);
1578
- else out.push(attr.value);
1579
+ if (attr.value !== void 0) {
1580
+ if (attr.name.startsWith("&")) out.push(`${attr.value} =`);
1581
+ else out.push(attr.value);
1582
+ }
1579
1583
  if (attr.fullyReactive && attr.name !== attr.value) out.push(attr.name);
1580
1584
  }
1581
1585
  for (const child of node.children) collectExpressionStrings(child, out);
@@ -2158,10 +2162,11 @@ function buildTitleNode(node, status, b) {
2158
2162
  }
2159
2163
  function buildHeadNode(node, status, b) {
2160
2164
  status.imports.add("$run");
2165
+ status.imports.add("t_head_element");
2166
+ const selector = headElementSelector(node);
2161
2167
  b.append("$run(() => {");
2162
2168
  b.append(`
2163
- const t_head_el = document.createElement("${node.tagName}");
2164
- document.getElementsByTagName("head")[0].appendChild(t_head_el);`);
2169
+ const t_head_el = t_head_element("${node.tagName}", ${JSON.stringify(selector)});`);
2165
2170
  for (let { name, value, span } of node.attributes) if (value != null) {
2166
2171
  status.imports.add("t_attribute");
2167
2172
  value = replaceForVarNames(value, status);
@@ -2169,31 +2174,55 @@ function buildHeadNode(node, status, b) {
2169
2174
  }
2170
2175
  b.append(`}${status.options.dev === true ? `, "runHead"` : ""});`);
2171
2176
  }
2177
+ /**
2178
+ * Builds a selector that finds an equivalent element in the head, from the
2179
+ * attributes that identify it (e.g. `meta[name="description"]`). Returns an
2180
+ * empty string when the element has no identifying attributes, in which case
2181
+ * a new element is always created.
2182
+ */
2183
+ function headElementSelector(node) {
2184
+ const identityAttributes = [
2185
+ "name",
2186
+ "property",
2187
+ "rel",
2188
+ "charset",
2189
+ "http-equiv",
2190
+ "itemprop"
2191
+ ];
2192
+ const parts = [];
2193
+ for (let { name, value, reactive } of node.attributes) {
2194
+ if (!identityAttributes.includes(name) || value == null || reactive) continue;
2195
+ const attributeValue = trimQuotes(value).replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
2196
+ parts.push(`[${name}="${attributeValue}"]`);
2197
+ }
2198
+ return parts.length ? node.tagName + parts.join("") : "";
2199
+ }
2172
2200
  function buildElementAttributes$1(node, varName, status, b) {
2173
2201
  let fragment = status.fragmentStack.at(-1)?.fragment;
2174
2202
  if (!fragment) throw new Error("No fragment on stack");
2175
- for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (value != null && fullyReactive) if (name === "&group") buildBindGroupAttribute(node, varName, value, status, b);
2176
- else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b);
2177
- else if (name === "onmount") buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
2178
- else if (name.startsWith("on")) buildEventAttribute(varName, name, value, span, status, b);
2179
- else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
2180
- else if (name === "class") {
2181
- status.imports.add("t_class");
2182
- const params = [value];
2183
- if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
2184
- const propName = status.ns ? "className.baseVal" : "className";
2185
- stashRun(fragment, `${status.ns ? "// @ts-ignore\n" : ""}${varName}.${propName} = t_class(`, params.join(", "), ");", span, status);
2186
- } else if (name === "style") {
2187
- status.imports.add("t_style");
2188
- stashRun(fragment, `${varName}.style.cssText += t_style(`, value, ");", span, status);
2189
- } else if (name.includes("-")) {
2190
- status.imports.add("t_attribute");
2191
- stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2192
- } else {
2193
- status.imports.add("t_attribute");
2194
- stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2195
- }
2196
- else if (value != null && reactive) {
2203
+ for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (value != null && fullyReactive) {
2204
+ if (name === "&group") buildBindGroupAttribute(node, varName, value, status, b);
2205
+ else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b);
2206
+ else if (name === "onmount") buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
2207
+ else if (name.startsWith("on")) buildEventAttribute(varName, name, value, span, status, b);
2208
+ else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
2209
+ else if (name === "class") {
2210
+ status.imports.add("t_class");
2211
+ const params = [value];
2212
+ if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
2213
+ const propName = status.ns ? "className.baseVal" : "className";
2214
+ stashRun(fragment, `${status.ns ? "// @ts-ignore\n" : ""}${varName}.${propName} = t_class(`, params.join(", "), ");", span, status);
2215
+ } else if (name === "style") {
2216
+ status.imports.add("t_style");
2217
+ stashRun(fragment, `${varName}.style.cssText += t_style(`, value, ");", span, status);
2218
+ } else if (name.includes("-")) {
2219
+ status.imports.add("t_attribute");
2220
+ stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2221
+ } else {
2222
+ status.imports.add("t_attribute");
2223
+ stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2224
+ }
2225
+ } else if (value != null && reactive) {
2197
2226
  const { newValue, spans, offsets, lengths } = getAttributeOffsets(value, span);
2198
2227
  value = newValue;
2199
2228
  if (name === "class") {
@@ -2245,7 +2274,6 @@ function buildBindAttribute(node, varName, name, value, status, b) {
2245
2274
  case "radio":
2246
2275
  eventName = "change";
2247
2276
  inputValue = "e.target.value";
2248
- break;
2249
2277
  }
2250
2278
  } else if (node.tagName === "select") {
2251
2279
  eventName = "change";
@@ -2288,10 +2316,12 @@ function buildTransitionAttribute(node, varName, name, value, status, b) {
2288
2316
  b.append(`const ${entryVarName} = (${value})(${varName});`);
2289
2317
  b.append(`t_animate(${varName}, ${entryVarName});`);
2290
2318
  }
2291
- } else if (name === "transition-out") if (node.attributes.find((a) => a.name === "transition-in")) return;
2292
- else {
2293
- b.append(`const ${exitVarName} = (${value})(${varName});`);
2294
- b.append(`t_animate(${varName}, null, ${entryVarName});`);
2319
+ } else if (name === "transition-out") {
2320
+ if (node.attributes.find((a) => a.name === "transition-in")) return;
2321
+ else {
2322
+ b.append(`const ${exitVarName} = (${value})(${varName});`);
2323
+ b.append(`t_animate(${varName}, null, ${entryVarName});`);
2324
+ }
2295
2325
  }
2296
2326
  }
2297
2327
  //#endregion
@@ -2504,6 +2534,7 @@ const importsMap$1 = {
2504
2534
  t_attribute: "import { t_attribute } from \"${folder}\";",
2505
2535
  t_dynamic: "import { t_dynamic } from \"${folder}\";",
2506
2536
  t_fmt: "import { t_fmt } from \"${folder}\";",
2537
+ t_head_element: "import { t_head_element } from \"${folder}\";",
2507
2538
  t_fragment: "import { t_fragment } from \"${folder}\";",
2508
2539
  t_fragment_el: "import { t_fragment_el } from \"${folder}\";",
2509
2540
  t_event: "import { t_event } from \"${folder}\";",
@@ -2715,13 +2746,22 @@ function buildAttributeNode(attribute, b) {
2715
2746
  b.append(`${attribute.name}: ${attribute.value};`);
2716
2747
  }
2717
2748
  //#endregion
2718
- //#region src/compile/build/server/buildServerComponentNode.ts
2719
- function buildServerComponentNode(node, status, b) {
2720
- status.output += HYDRATION_START_COMMENT;
2749
+ //#region src/compile/build/server/flushOutput.ts
2750
+ /**
2751
+ * Appends any pending output to the generated code, targeting the body
2752
+ * string -- or the head string, when building inside a @head block.
2753
+ */
2754
+ function flushOutput(status, b) {
2721
2755
  if (status.output) {
2722
- b.append(`t_body += \`${status.output}\`;`);
2756
+ b.append(`${status.inHead ? "t_head" : "t_body"} += \`${status.output}\`;`);
2723
2757
  status.output = "";
2724
2758
  }
2759
+ }
2760
+ //#endregion
2761
+ //#region src/compile/build/server/buildServerComponentNode.ts
2762
+ function buildServerComponentNode(node, status, b) {
2763
+ status.output += HYDRATION_START_COMMENT;
2764
+ flushOutput(status, b);
2725
2765
  const componentHasProps = node.attributes.length;
2726
2766
  const propsName = componentHasProps ? nextVarName("props", status) : "undefined";
2727
2767
  if (componentHasProps) {
@@ -2759,7 +2799,7 @@ function buildServerComponentNode(node, status, b) {
2759
2799
  const nameAttribute = slot.attributes.find((a) => a.name === "name");
2760
2800
  const slotName = nameAttribute?.value ? trimQuotes(nameAttribute.value) : "_";
2761
2801
  const slotParams = [slot.hasSlotProps ? "$slot: Record<PropertyKey, any>" : "_$slot?: Record<PropertyKey, any>", "// @ts-ignore\n// eslint-disable-next-line no-unused-vars\n$context?: Record<PropertyKey, any>"];
2762
- b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = (\n${slotParams.join(",\n")}\n) => {`);
2802
+ b.append(`${slot.hasSlotProps ? "// @ts-ignore\n" : ""}${slotsName}["${slotName}"] = async (\n${slotParams.join(",\n")}\n) => {`);
2763
2803
  b.append(`let t_body = "";`);
2764
2804
  for (let child of slot.children) buildServerNode(child, status, b);
2765
2805
  if (status.output) {
@@ -2778,39 +2818,51 @@ function buildServerComponentNode(node, status, b) {
2778
2818
  let renderParams = `${propsName}, $context`;
2779
2819
  if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
2780
2820
  const componentResult = nextVarName("comp", status);
2781
- b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
2782
- b.append(`t_body += ${componentResult}.body;`);
2821
+ b.append(`const ${componentResult} = await ${componentName}(${renderParams});`);
2822
+ b.append(`${status.inHead ? "t_head" : "t_body"} += ${componentResult}.body;`);
2783
2823
  b.append(`t_head += ${componentResult}.head;`);
2784
2824
  status.output += HYDRATION_END_COMMENT;
2785
2825
  status.output += "<!>";
2786
2826
  }
2787
2827
  //#endregion
2788
2828
  //#region src/compile/build/server/buildServerAwaitNode.ts
2829
+ /**
2830
+ * Builds an `@await` boundary for the server (ASYNC.md → "Server rendering").
2831
+ *
2832
+ * Rather than inlining the `with` branch (the old shape), the boundary is
2833
+ * handed to the `t_await_server` runtime helper: content and `with` branches
2834
+ * become closures, and the helper decides at render time whether resolved
2835
+ * content can ship (all `source: "server"` reads settled) or the `with`
2836
+ * branch + client fetch is the honest output. The runtime emits the
2837
+ * hydration markers and anchor itself, including the `<!--t-await:...-->`
2838
+ * values payload for resolved boundaries.
2839
+ */
2789
2840
  function buildServerAwaitNode(node, status, b) {
2790
- status.output += HYDRATION_START_COMMENT;
2791
- if (status.output) {
2792
- b.append(`t_body += \`${status.output}\`;`);
2793
- status.output = "";
2794
- }
2795
- for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") buildServerAwaitBranch(branch, status, b);
2796
- status.output += HYDRATION_END_COMMENT;
2797
- status.output += "<!>";
2798
- }
2799
- function buildServerAwaitBranch(node, status, b) {
2800
- for (let child of node.children) buildServerNode(child, status, b);
2801
- if (status.output) {
2802
- b.append(`t_body += \`${status.output}\`;`);
2803
- status.output = "";
2804
- }
2841
+ flushOutput(status, b);
2842
+ status.imports.add("t_await_server");
2843
+ const id = status.awaitCount++;
2844
+ const resultName = nextVarName("await", status);
2845
+ b.append(`const ${resultName} = await t_await_server("${id}", async () => {`);
2846
+ b.append(`let t_body = "";`);
2847
+ b.append(`let t_head = "";`);
2848
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@await") for (let child of branch.children) buildServerNode(child, status, b);
2849
+ flushOutput(status, b);
2850
+ b.append(`return { body: t_body, head: t_head };`);
2851
+ b.append(`}, async () => {`);
2852
+ b.append(`let t_body = "";`);
2853
+ b.append(`let t_head = "";`);
2854
+ for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") for (let child of branch.children) buildServerNode(child, status, b);
2855
+ flushOutput(status, b);
2856
+ b.append(`return { body: t_body, head: t_head };`);
2857
+ b.append(`});`);
2858
+ b.append(`${status.inHead ? "t_head" : "t_body"} += ${resultName}.body;`);
2859
+ b.append(`t_head += ${resultName}.head;`);
2805
2860
  }
2806
2861
  //#endregion
2807
2862
  //#region src/compile/build/server/buildServerForNode.ts
2808
2863
  function buildServerForNode(node, status, b) {
2809
2864
  status.output += HYDRATION_START_COMMENT;
2810
- if (status.output) {
2811
- b.append(`t_body += \`${status.output}\`;`);
2812
- status.output = "";
2813
- }
2865
+ flushOutput(status, b);
2814
2866
  for (let branch of node.children) if (isControlNode(branch)) buildServerForBranch(branch, status, b);
2815
2867
  status.output += HYDRATION_END_COMMENT;
2816
2868
  status.output += "<!>";
@@ -2819,10 +2871,7 @@ function buildServerForBranch(node, status, b) {
2819
2871
  b.append(`${node.statement} {`);
2820
2872
  status.output += HYDRATION_BREAK_COMMENT;
2821
2873
  for (let child of node.children) buildServerNode(child, status, b);
2822
- if (status.output) {
2823
- b.append(`t_body += \`${status.output}\`;`);
2824
- status.output = "";
2825
- }
2874
+ flushOutput(status, b);
2826
2875
  b.append("}");
2827
2876
  }
2828
2877
  //#endregion
@@ -2837,10 +2886,7 @@ function buildServerHtmlNode(node, status) {
2837
2886
  //#region src/compile/build/server/buildServerIfNode.ts
2838
2887
  function buildServerIfNode(node, status, b) {
2839
2888
  status.output += HYDRATION_START_COMMENT;
2840
- if (status.output) {
2841
- b.append(`t_body += \`${status.output}\`;`);
2842
- status.output = "";
2843
- }
2889
+ flushOutput(status, b);
2844
2890
  for (let branch of node.children) if (isControlNode(branch)) buildServerIfBranch(branch, status, b);
2845
2891
  status.output += HYDRATION_END_COMMENT;
2846
2892
  status.output += "<!>";
@@ -2849,20 +2895,14 @@ function buildServerIfBranch(node, status, b) {
2849
2895
  b.append(`${node.statement} {`);
2850
2896
  status.output += HYDRATION_BREAK_COMMENT;
2851
2897
  for (let child of node.children) buildServerNode(child, status, b);
2852
- if (status.output) {
2853
- b.append(`t_body += \`${status.output}\`;`);
2854
- status.output = "";
2855
- }
2898
+ flushOutput(status, b);
2856
2899
  b.append("}");
2857
2900
  }
2858
2901
  //#endregion
2859
2902
  //#region src/compile/build/server/buildServerReplaceNode.ts
2860
2903
  function buildServerReplaceNode(node, status, b) {
2861
2904
  status.output += HYDRATION_START_COMMENT;
2862
- if (status.output) {
2863
- b.append(`t_body += \`${status.output}\`;`);
2864
- status.output = "";
2865
- }
2905
+ flushOutput(status, b);
2866
2906
  for (let branch of node.children) if (isControlNode(branch)) buildServerReplaceBranch(branch, status, b);
2867
2907
  status.output += HYDRATION_END_COMMENT;
2868
2908
  status.output += "<!>";
@@ -2870,10 +2910,7 @@ function buildServerReplaceNode(node, status, b) {
2870
2910
  function buildServerReplaceBranch(node, status, b) {
2871
2911
  b.append(`${node.statement};`);
2872
2912
  for (let child of node.children) buildServerNode(child, status, b);
2873
- if (status.output) {
2874
- b.append(`t_body += \`${status.output}\`;`);
2875
- status.output = "";
2876
- }
2913
+ flushOutput(status, b);
2877
2914
  }
2878
2915
  //#endregion
2879
2916
  //#region src/compile/build/server/buildServerScriptNode.ts
@@ -2888,10 +2925,7 @@ function maybeAppend(text, end) {
2888
2925
  //#region src/compile/build/server/buildServerSwitchNode.ts
2889
2926
  function buildServerSwitchNode(node, status, b) {
2890
2927
  status.output += HYDRATION_START_COMMENT;
2891
- if (status.output) {
2892
- b.append(`t_body += \`${status.output}\`;`);
2893
- status.output = "";
2894
- }
2928
+ flushOutput(status, b);
2895
2929
  b.append(`${node.statement} {`);
2896
2930
  for (let branch of node.children.filter((c) => isControlNode(c))) if (isControlNode(branch)) buildServerSwitchBranch(branch, status, b);
2897
2931
  b.append("}");
@@ -2902,10 +2936,7 @@ function buildServerSwitchBranch(node, status, b) {
2902
2936
  b.append(`${node.statement} {`);
2903
2937
  status.output += HYDRATION_BREAK_COMMENT;
2904
2938
  for (let child of node.children) buildServerNode(child, status, b);
2905
- if (status.output) {
2906
- b.append(`t_body += \`${status.output}\`;`);
2907
- status.output = "";
2908
- }
2939
+ flushOutput(status, b);
2909
2940
  b.append("break;");
2910
2941
  b.append("}");
2911
2942
  }
@@ -2913,10 +2944,7 @@ function buildServerSwitchBranch(node, status, b) {
2913
2944
  //#region src/compile/build/server/buildServerTryNode.ts
2914
2945
  function buildServerTryNode(node, status, b) {
2915
2946
  status.output += HYDRATION_START_COMMENT;
2916
- if (status.output) {
2917
- b.append(`t_body += \`${status.output}\`;`);
2918
- status.output = "";
2919
- }
2947
+ flushOutput(status, b);
2920
2948
  const branches = node.children.filter((n) => isControlNode(n));
2921
2949
  let tryBranch = branches.find((n) => n.operation === "@try");
2922
2950
  let catchBranch = branches.find((n) => n.operation === "@catch");
@@ -2937,10 +2965,7 @@ function buildServerTryNode(node, status, b) {
2937
2965
  }
2938
2966
  function buildServerTryBranch(node, status, b) {
2939
2967
  for (let child of node.children) buildServerNode(child, status, b);
2940
- if (status.output) {
2941
- b.append(`t_body += \`${status.output}\`;`);
2942
- status.output = "";
2943
- }
2968
+ flushOutput(status, b);
2944
2969
  }
2945
2970
  //#endregion
2946
2971
  //#region src/compile/build/server/buildServerControlNode.ts
@@ -2981,18 +3006,12 @@ function buildServerControlNode(node, status, b) {
2981
3006
  break;
2982
3007
  case "@html": break;
2983
3008
  case "@const":
2984
- if (status.output) {
2985
- b.append(`t_body += \`${status.output}\`;`);
2986
- status.output = "";
2987
- }
3009
+ flushOutput(status, b);
2988
3010
  buildServerScriptNode(node, b);
2989
3011
  break;
2990
3012
  case "@function":
2991
3013
  case "@async function":
2992
- if (status.output) {
2993
- b.append(`t_body += \`${status.output}\`;`);
2994
- status.output = "";
2995
- }
3014
+ flushOutput(status, b);
2996
3015
  b.append("");
2997
3016
  buildServerScriptNode(node, b);
2998
3017
  b.append("");
@@ -3022,48 +3041,47 @@ function buildServerElementNode(node, status, b) {
3022
3041
  }
3023
3042
  }
3024
3043
  function buildElementAttributes(node, status) {
3025
- let needsClass = node.scopeStyles;
3044
+ let needsClass = node.scopeStyles && !status.inHead;
3026
3045
  let attributes = [];
3027
- for (let { name, value, reactive, fullyReactive } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (name.startsWith("on") || name.startsWith(":on")) {} else if (name.startsWith("transition") && value) {} else if (value != null && fullyReactive) if (name === "&value" || name === "&checked" || name === "&group") {
3028
- if (name === "&value" && node.tagName === "select" && node.attributes.some((a) => a.name === "multiple")) continue;
3029
- let defaultValue = "";
3030
- let typeAttribute = node.attributes.find((a) => a.name === "type");
3031
- if (typeAttribute && typeAttribute.value) switch (trimQuotes(typeAttribute.value)) {
3032
- case "number":
3033
- defaultValue = "0";
3034
- break;
3035
- case "checkbox":
3036
- defaultValue = "false";
3037
- break;
3038
- }
3039
- if (!defaultValue) {
3046
+ for (let { name, value, reactive, fullyReactive } of node.attributes) if (name === "self" && node.tagName === "@element") {} else if (name === "&ref") {} else if (name.startsWith("on") || name.startsWith(":on")) {} else if (name.startsWith("transition") && value) {} else if (value != null && fullyReactive) {
3047
+ if (name === "&value" || name === "&checked" || name === "&group") {
3048
+ if (name === "&value" && node.tagName === "select" && node.attributes.some((a) => a.name === "multiple")) continue;
3049
+ let defaultValue = "";
3050
+ let typeAttribute = node.attributes.find((a) => a.name === "type");
3051
+ if (typeAttribute && typeAttribute.value) switch (trimQuotes(typeAttribute.value)) {
3052
+ case "number":
3053
+ defaultValue = "0";
3054
+ break;
3055
+ case "checkbox": defaultValue = "false";
3056
+ }
3057
+ if (!defaultValue) {
3058
+ status.imports.add("t_attr");
3059
+ value = `t_attr(${value})`;
3060
+ defaultValue = "\"\"";
3061
+ }
3062
+ let valueOrDefault = `${value} || ${defaultValue}`;
3063
+ const propName = name.substring(1);
3064
+ attributes.push(`${propName}="\${${valueOrDefault}}"`);
3065
+ } else if (name === "class") {
3066
+ status.imports.add("t_class");
3067
+ let params = [value];
3068
+ if (node.scopeStyles && !status.inHead) {
3069
+ params.push(`"torp-${status.styleHash}"`);
3070
+ needsClass = false;
3071
+ }
3072
+ value = params.join(", ");
3073
+ attributes.push(`\${t_class(${value}) !== "" ? \`class="\${t_class(${value})}"\` : ""}`);
3074
+ } else if (name === "style") {
3075
+ status.imports.add("t_style");
3076
+ attributes.push(`\${t_style(${value}) !== "" ? \`style="\${t_style(${value})}"\` : ""}`);
3077
+ } else {
3040
3078
  status.imports.add("t_attr");
3041
- value = `t_attr(${value})`;
3042
- defaultValue = "\"\"";
3079
+ attributes.push(`\${${value} ? \`${name}="\${t_attr(${value})}"\` : ""}`);
3043
3080
  }
3044
- let valueOrDefault = `${value} || ${defaultValue}`;
3045
- const propName = name.substring(1);
3046
- attributes.push(`${propName}="\${${valueOrDefault}}"`);
3047
- } else if (name === "class") {
3048
- status.imports.add("t_class");
3049
- let params = [value];
3050
- if (node.scopeStyles) {
3051
- params.push(`"torp-${status.styleHash}"`);
3052
- needsClass = false;
3053
- }
3054
- value = params.join(", ");
3055
- attributes.push(`\${t_class(${value}) !== "" ? \`class="\${t_class(${value})}"\` : ""}`);
3056
- } else if (name === "style") {
3057
- status.imports.add("t_style");
3058
- attributes.push(`\${t_style(${value}) !== "" ? \`style="\${t_style(${value})}"\` : ""}`);
3059
- } else {
3060
- status.imports.add("t_attr");
3061
- attributes.push(`\${${value} ? \`${name}="\${t_attr(${value})}"\` : ""}`);
3062
- }
3063
- else if (value != null && reactive) {
3081
+ } else if (value != null && reactive) {
3064
3082
  status.imports.add("t_attr");
3065
3083
  attributes.push(`${name}="${trimQuotes(value).replaceAll("{", "${t_attr(").replaceAll("}", ")}")}"`);
3066
- } else if (name === "class" && node.scopeStyles) {
3084
+ } else if (name === "class" && node.scopeStyles && !status.inHead) {
3067
3085
  value = value ? `"${trimQuotes(value)} torp-${status.styleHash}"` : `"torp-${status.styleHash}"`;
3068
3086
  attributes.push(`${name}="${trimQuotes(value).replaceAll("\"", "&quot;")}"`);
3069
3087
  needsClass = false;
@@ -3081,10 +3099,7 @@ function buildServerRootNode(node, status, b) {
3081
3099
  //#region src/compile/build/server/buildServerSlotNode.ts
3082
3100
  function buildServerSlotNode(node, status, b) {
3083
3101
  status.output += HYDRATION_START_COMMENT;
3084
- if (status.output) {
3085
- b.append(`t_body += \`${status.output}\`;`);
3086
- status.output = "";
3087
- }
3102
+ flushOutput(status, b);
3088
3103
  let slotName = node.attributes.find((a) => a.name === "name")?.value;
3089
3104
  slotName = slotName ? trimQuotes(slotName) : "_";
3090
3105
  const propsName = nextVarName("slot_props", status);
@@ -3096,15 +3111,12 @@ function buildServerSlotNode(node, status, b) {
3096
3111
  else b.append(`${propsName}["${name}"] = true;`);
3097
3112
  }
3098
3113
  b.append(`if ($slots && $slots["${slotName}"]) {`);
3099
- b.append(`t_body += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3114
+ b.append(`${status.inHead ? "t_head" : "t_body"} += await $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3100
3115
  const fill = node.children.find((c) => isSpecialNode(c) && (c.tagName === "fill" || c.tagName === "filldef"));
3101
3116
  if (fill && isSpecialNode(fill) && fill.children.length) {
3102
3117
  b.append(`} else {`);
3103
3118
  for (let child of fill.children) buildServerNode(child, status, b);
3104
- if (status.output) {
3105
- b.append(`t_body += \`${status.output}\`;`);
3106
- status.output = "";
3107
- }
3119
+ flushOutput(status, b);
3108
3120
  }
3109
3121
  b.append(`}`);
3110
3122
  status.output += HYDRATION_END_COMMENT;
@@ -3202,6 +3214,8 @@ const importsMap = {
3202
3214
  $unwrap: "import { $unwrap } from \"${folder}\";",
3203
3215
  $peek: "import { $peek } from \"${folder}\";",
3204
3216
  $batch: "import { $batch } from \"${folder}\";",
3217
+ t_server_flush: "import { t_server_flush } from \"${folder}\";",
3218
+ t_await_server: "import { t_await_server } from \"${folder}\";",
3205
3219
  t_fmt: "import { t_fmt } from \"${folder}\";",
3206
3220
  t_attr: "import { t_attr } from \"${folder}\";",
3207
3221
  t_class: "import { t_class } from \"${folder}\";",
@@ -3244,18 +3258,24 @@ function buildServerTemplate(template, imports, b, options) {
3244
3258
  if (/\$unwrap\b/.test(scriptCode)) imports.add("$unwrap");
3245
3259
  if (/\$peek\b/.test(scriptCode)) imports.add("$peek");
3246
3260
  if (/\$batch\b/.test(scriptCode)) imports.add("$batch");
3261
+ const asyncChunks = /* @__PURE__ */ new Set();
3262
+ for (let [i, chunk] of template.script.entries()) if (chunk.script === "/* @params */" && i > 0) asyncChunks.add(i - 1);
3247
3263
  let currentIndex = 0;
3248
3264
  let current = template.components[0];
3249
- for (let chunk of template.script) if (chunk.script === "/* @params */") {
3265
+ for (let [chunkIndex, chunk] of template.script.entries()) if (chunk.script === "/* @params */") {
3250
3266
  let params = [
3251
3267
  current.params ?? `${current.props?.length ? "$props: Record<PropertyKey, any>" : "_$props?: Record<PropertyKey, any>"}`,
3252
3268
  `${current.contextProps?.length || current.markup && markupRendersComponent(current.markup) || current.error && markupRendersComponent(current.error) ? "$context" : "_$context"}?: Record<PropertyKey, any>`,
3253
3269
  `${current.slotProps?.length ? "$slots" : "_$slots"}?: Record<string, ServerSlotRender>`
3254
3270
  ];
3255
3271
  b.append(params.join(",\n") + ",");
3256
- } else if (chunk.script === ") /* @return_type */ {") b.append("): { body: string; head: string } {");
3272
+ } else if (chunk.script === ") /* @return_type */ {") b.append("): Promise<{ body: string; head: string }> {");
3257
3273
  else if (chunk.script === "/* @start */") {
3258
3274
  if (current.contextProps?.length) b.append(`$context = Object.assign({}, $context);`);
3275
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) {
3276
+ imports.add("t_server_flush");
3277
+ b.append(`return t_server_flush(async () => {`);
3278
+ }
3259
3279
  b.append(`let t_body = "";`);
3260
3280
  b.append(`let t_head = "";`);
3261
3281
  } else if (chunk.script === "/* @render */") {
@@ -3266,6 +3286,7 @@ function buildServerTemplate(template, imports, b, options) {
3266
3286
  styleHash: current.style?.hash || "",
3267
3287
  varNames: {},
3268
3288
  preserveWhitespace: false,
3289
+ awaitCount: 0,
3269
3290
  options
3270
3291
  };
3271
3292
  b.append("");
@@ -3289,6 +3310,7 @@ function buildServerTemplate(template, imports, b, options) {
3289
3310
  styleHash: current.style?.hash || "",
3290
3311
  varNames: {},
3291
3312
  preserveWhitespace: false,
3313
+ awaitCount: 0,
3292
3314
  options
3293
3315
  };
3294
3316
  b.append(`} catch (${current.errorVar}) {`);
@@ -3302,7 +3324,24 @@ function buildServerTemplate(template, imports, b, options) {
3302
3324
  }
3303
3325
  b.append("}");
3304
3326
  }
3305
- } else if (chunk.script === "/* @head */") {} else if (chunk.script === "/* @style */") {
3327
+ } else if (chunk.script === "/* @head */") {
3328
+ if (current.head) {
3329
+ const status = {
3330
+ imports,
3331
+ output: "",
3332
+ styleHash: current.style?.hash || "",
3333
+ varNames: {},
3334
+ preserveWhitespace: false,
3335
+ inHead: true,
3336
+ awaitCount: 0,
3337
+ options
3338
+ };
3339
+ b.append("");
3340
+ b.append("/* Head */");
3341
+ buildServerNode(current.head, status, b);
3342
+ flushOutput(status, b);
3343
+ }
3344
+ } else if (chunk.script === "/* @style */") {
3306
3345
  if (current.style) {
3307
3346
  b.append("");
3308
3347
  b.append("/* Style */");
@@ -3311,9 +3350,23 @@ function buildServerTemplate(template, imports, b, options) {
3311
3350
  }
3312
3351
  } else if (chunk.script === "/* @end */") {
3313
3352
  b.append(`return { body: t_body, head: t_head };`);
3353
+ if (containsAwaitGroup(current.markup) || containsAwaitGroup(current.error) || containsAwaitGroup(current.head)) b.append(`});`);
3314
3354
  currentIndex += 1;
3315
3355
  current = template.components[currentIndex];
3316
- } else b.append(chunk.script);
3356
+ } else if (asyncChunks.has(chunkIndex)) b.append(chunk.script.replace(/(?<!async\s)function\s+([A-Za-z_$][\w$]*)((?:<(?:[^<>]|<[^<>]*>)*>)?)\s*\($/, "async function $1$2("));
3357
+ else b.append(chunk.script);
3358
+ }
3359
+ /**
3360
+ * Whether a markup tree contains an `@await` boundary, so the component's
3361
+ * render must be wrapped in a flush.
3362
+ */
3363
+ function containsAwaitGroup(node) {
3364
+ if (node === void 0 || node === null) return false;
3365
+ if (isControlNode(node) && node.operation === "@await group") return true;
3366
+ if (node.type !== "text" && node.type !== "comment") {
3367
+ for (let child of node.children) if (containsAwaitGroup(child)) return true;
3368
+ }
3369
+ return false;
3317
3370
  }
3318
3371
  //#endregion
3319
3372
  //#region src/compile/utils/trimWhitespace.ts
@@ -3531,9 +3584,10 @@ function parseInlineScript(status) {
3531
3584
  continue;
3532
3585
  }
3533
3586
  if (accept("{", status)) braceCount += 1;
3534
- else if (accept("}", status)) if (braceCount > 0) braceCount -= 1;
3535
- else return status.source.substring(start, status.i - 1);
3536
- else status.i += 1;
3587
+ else if (accept("}", status)) {
3588
+ if (braceCount > 0) braceCount -= 1;
3589
+ else return status.source.substring(start, status.i - 1);
3590
+ } else status.i += 1;
3537
3591
  }
3538
3592
  return "";
3539
3593
  }
@@ -3889,9 +3943,10 @@ function usesSlot(nodes) {
3889
3943
  */
3890
3944
  function slottifyChildNodes(node) {
3891
3945
  let nonFillNodes = [];
3892
- for (let child of node.children) if (isFillNode(child)) if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3893
- else break;
3894
- else nonFillNodes.push(child);
3946
+ for (let child of node.children) if (isFillNode(child)) {
3947
+ if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3948
+ else break;
3949
+ } else nonFillNodes.push(child);
3895
3950
  if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3896
3951
  if (nonFillNodes.length) {
3897
3952
  let defaultFillNode = node.children.find((n) => isFillNode(n) && !n.attributes.find((a) => a.name === "name"));
@@ -4377,11 +4432,12 @@ function hash(input) {
4377
4432
  * @returns True if the supplied string was found at the current parse position
4378
4433
  */
4379
4434
  function expect(value, status, advance = true) {
4380
- if (status.i < status.source.length) if (status.source.substring(status.i, status.i + value.length) == value) {
4381
- status.i += advance ? value.length : 0;
4382
- return true;
4383
- } else addError(status, `Expected ${value}`);
4384
- else addError(status, "Expected token");
4435
+ if (status.i < status.source.length) {
4436
+ if (status.source.substring(status.i, status.i + value.length) == value) {
4437
+ status.i += advance ? value.length : 0;
4438
+ return true;
4439
+ } else addError(status, `Expected ${value}`);
4440
+ } else addError(status, "Expected token");
4385
4441
  return false;
4386
4442
  }
4387
4443
  //#endregion
@@ -4532,21 +4588,25 @@ function parseCode(source) {
4532
4588
  status.level = 0;
4533
4589
  }
4534
4590
  } else if (accept("function", status)) {
4535
- if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
4536
- else status.i--;
4591
+ if (consumeSpace(status)) {
4592
+ if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
4593
+ else status.i--;
4594
+ }
4537
4595
  } else if (accept("const", status)) {
4538
- if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) {
4539
- const start = status.i;
4540
- consumeAlphaNumeric(status);
4541
- consumeSpace(status);
4542
- if (accept("=", status)) {
4596
+ if (consumeSpace(status)) {
4597
+ if (/[A-Z]/.test(source[status.i])) {
4598
+ const start = status.i;
4599
+ consumeAlphaNumeric(status);
4543
4600
  consumeSpace(status);
4544
- if (accept("(", status, false)) {
4545
- status.i = start;
4546
- parseComponentStart(status);
4601
+ if (accept("=", status)) {
4602
+ consumeSpace(status);
4603
+ if (accept("(", status, false)) {
4604
+ status.i = start;
4605
+ parseComponentStart(status);
4606
+ }
4547
4607
  }
4548
- }
4549
- } else status.i--;
4608
+ } else status.i--;
4609
+ }
4550
4610
  } else if (accept("export", status)) {
4551
4611
  const start = status.i;
4552
4612
  if (consumeSpace(status) && accept("default", status) && consumeSpace(status) && accept("(", status, false)) parseComponentStart(status);