@torpor/view 1.0.2 → 1.0.3

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.
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) {
@@ -2779,7 +2819,7 @@ function buildServerComponentNode(node, status, b) {
2779
2819
  if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
2780
2820
  const componentResult = nextVarName("comp", status);
2781
2821
  b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
2782
- b.append(`t_body += ${componentResult}.body;`);
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 += "<!>";
@@ -2788,29 +2828,20 @@ function buildServerComponentNode(node, status, b) {
2788
2828
  //#region src/compile/build/server/buildServerAwaitNode.ts
2789
2829
  function buildServerAwaitNode(node, status, b) {
2790
2830
  status.output += HYDRATION_START_COMMENT;
2791
- if (status.output) {
2792
- b.append(`t_body += \`${status.output}\`;`);
2793
- status.output = "";
2794
- }
2831
+ flushOutput(status, b);
2795
2832
  for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") buildServerAwaitBranch(branch, status, b);
2796
2833
  status.output += HYDRATION_END_COMMENT;
2797
2834
  status.output += "<!>";
2798
2835
  }
2799
2836
  function buildServerAwaitBranch(node, status, b) {
2800
2837
  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
- }
2838
+ flushOutput(status, b);
2805
2839
  }
2806
2840
  //#endregion
2807
2841
  //#region src/compile/build/server/buildServerForNode.ts
2808
2842
  function buildServerForNode(node, status, b) {
2809
2843
  status.output += HYDRATION_START_COMMENT;
2810
- if (status.output) {
2811
- b.append(`t_body += \`${status.output}\`;`);
2812
- status.output = "";
2813
- }
2844
+ flushOutput(status, b);
2814
2845
  for (let branch of node.children) if (isControlNode(branch)) buildServerForBranch(branch, status, b);
2815
2846
  status.output += HYDRATION_END_COMMENT;
2816
2847
  status.output += "<!>";
@@ -2819,10 +2850,7 @@ function buildServerForBranch(node, status, b) {
2819
2850
  b.append(`${node.statement} {`);
2820
2851
  status.output += HYDRATION_BREAK_COMMENT;
2821
2852
  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
- }
2853
+ flushOutput(status, b);
2826
2854
  b.append("}");
2827
2855
  }
2828
2856
  //#endregion
@@ -2837,10 +2865,7 @@ function buildServerHtmlNode(node, status) {
2837
2865
  //#region src/compile/build/server/buildServerIfNode.ts
2838
2866
  function buildServerIfNode(node, status, b) {
2839
2867
  status.output += HYDRATION_START_COMMENT;
2840
- if (status.output) {
2841
- b.append(`t_body += \`${status.output}\`;`);
2842
- status.output = "";
2843
- }
2868
+ flushOutput(status, b);
2844
2869
  for (let branch of node.children) if (isControlNode(branch)) buildServerIfBranch(branch, status, b);
2845
2870
  status.output += HYDRATION_END_COMMENT;
2846
2871
  status.output += "<!>";
@@ -2849,20 +2874,14 @@ function buildServerIfBranch(node, status, b) {
2849
2874
  b.append(`${node.statement} {`);
2850
2875
  status.output += HYDRATION_BREAK_COMMENT;
2851
2876
  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
- }
2877
+ flushOutput(status, b);
2856
2878
  b.append("}");
2857
2879
  }
2858
2880
  //#endregion
2859
2881
  //#region src/compile/build/server/buildServerReplaceNode.ts
2860
2882
  function buildServerReplaceNode(node, status, b) {
2861
2883
  status.output += HYDRATION_START_COMMENT;
2862
- if (status.output) {
2863
- b.append(`t_body += \`${status.output}\`;`);
2864
- status.output = "";
2865
- }
2884
+ flushOutput(status, b);
2866
2885
  for (let branch of node.children) if (isControlNode(branch)) buildServerReplaceBranch(branch, status, b);
2867
2886
  status.output += HYDRATION_END_COMMENT;
2868
2887
  status.output += "<!>";
@@ -2870,10 +2889,7 @@ function buildServerReplaceNode(node, status, b) {
2870
2889
  function buildServerReplaceBranch(node, status, b) {
2871
2890
  b.append(`${node.statement};`);
2872
2891
  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
- }
2892
+ flushOutput(status, b);
2877
2893
  }
2878
2894
  //#endregion
2879
2895
  //#region src/compile/build/server/buildServerScriptNode.ts
@@ -2888,10 +2904,7 @@ function maybeAppend(text, end) {
2888
2904
  //#region src/compile/build/server/buildServerSwitchNode.ts
2889
2905
  function buildServerSwitchNode(node, status, b) {
2890
2906
  status.output += HYDRATION_START_COMMENT;
2891
- if (status.output) {
2892
- b.append(`t_body += \`${status.output}\`;`);
2893
- status.output = "";
2894
- }
2907
+ flushOutput(status, b);
2895
2908
  b.append(`${node.statement} {`);
2896
2909
  for (let branch of node.children.filter((c) => isControlNode(c))) if (isControlNode(branch)) buildServerSwitchBranch(branch, status, b);
2897
2910
  b.append("}");
@@ -2902,10 +2915,7 @@ function buildServerSwitchBranch(node, status, b) {
2902
2915
  b.append(`${node.statement} {`);
2903
2916
  status.output += HYDRATION_BREAK_COMMENT;
2904
2917
  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
- }
2918
+ flushOutput(status, b);
2909
2919
  b.append("break;");
2910
2920
  b.append("}");
2911
2921
  }
@@ -2913,10 +2923,7 @@ function buildServerSwitchBranch(node, status, b) {
2913
2923
  //#region src/compile/build/server/buildServerTryNode.ts
2914
2924
  function buildServerTryNode(node, status, b) {
2915
2925
  status.output += HYDRATION_START_COMMENT;
2916
- if (status.output) {
2917
- b.append(`t_body += \`${status.output}\`;`);
2918
- status.output = "";
2919
- }
2926
+ flushOutput(status, b);
2920
2927
  const branches = node.children.filter((n) => isControlNode(n));
2921
2928
  let tryBranch = branches.find((n) => n.operation === "@try");
2922
2929
  let catchBranch = branches.find((n) => n.operation === "@catch");
@@ -2937,10 +2944,7 @@ function buildServerTryNode(node, status, b) {
2937
2944
  }
2938
2945
  function buildServerTryBranch(node, status, b) {
2939
2946
  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
- }
2947
+ flushOutput(status, b);
2944
2948
  }
2945
2949
  //#endregion
2946
2950
  //#region src/compile/build/server/buildServerControlNode.ts
@@ -2981,18 +2985,12 @@ function buildServerControlNode(node, status, b) {
2981
2985
  break;
2982
2986
  case "@html": break;
2983
2987
  case "@const":
2984
- if (status.output) {
2985
- b.append(`t_body += \`${status.output}\`;`);
2986
- status.output = "";
2987
- }
2988
+ flushOutput(status, b);
2988
2989
  buildServerScriptNode(node, b);
2989
2990
  break;
2990
2991
  case "@function":
2991
2992
  case "@async function":
2992
- if (status.output) {
2993
- b.append(`t_body += \`${status.output}\`;`);
2994
- status.output = "";
2995
- }
2993
+ flushOutput(status, b);
2996
2994
  b.append("");
2997
2995
  buildServerScriptNode(node, b);
2998
2996
  b.append("");
@@ -3022,48 +3020,47 @@ function buildServerElementNode(node, status, b) {
3022
3020
  }
3023
3021
  }
3024
3022
  function buildElementAttributes(node, status) {
3025
- let needsClass = node.scopeStyles;
3023
+ let needsClass = node.scopeStyles && !status.inHead;
3026
3024
  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) {
3025
+ 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) {
3026
+ if (name === "&value" || name === "&checked" || name === "&group") {
3027
+ if (name === "&value" && node.tagName === "select" && node.attributes.some((a) => a.name === "multiple")) continue;
3028
+ let defaultValue = "";
3029
+ let typeAttribute = node.attributes.find((a) => a.name === "type");
3030
+ if (typeAttribute && typeAttribute.value) switch (trimQuotes(typeAttribute.value)) {
3031
+ case "number":
3032
+ defaultValue = "0";
3033
+ break;
3034
+ case "checkbox": defaultValue = "false";
3035
+ }
3036
+ if (!defaultValue) {
3037
+ status.imports.add("t_attr");
3038
+ value = `t_attr(${value})`;
3039
+ defaultValue = "\"\"";
3040
+ }
3041
+ let valueOrDefault = `${value} || ${defaultValue}`;
3042
+ const propName = name.substring(1);
3043
+ attributes.push(`${propName}="\${${valueOrDefault}}"`);
3044
+ } else if (name === "class") {
3045
+ status.imports.add("t_class");
3046
+ let params = [value];
3047
+ if (node.scopeStyles && !status.inHead) {
3048
+ params.push(`"torp-${status.styleHash}"`);
3049
+ needsClass = false;
3050
+ }
3051
+ value = params.join(", ");
3052
+ attributes.push(`\${t_class(${value}) !== "" ? \`class="\${t_class(${value})}"\` : ""}`);
3053
+ } else if (name === "style") {
3054
+ status.imports.add("t_style");
3055
+ attributes.push(`\${t_style(${value}) !== "" ? \`style="\${t_style(${value})}"\` : ""}`);
3056
+ } else {
3040
3057
  status.imports.add("t_attr");
3041
- value = `t_attr(${value})`;
3042
- defaultValue = "\"\"";
3043
- }
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;
3058
+ attributes.push(`\${${value} ? \`${name}="\${t_attr(${value})}"\` : ""}`);
3053
3059
  }
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) {
3060
+ } else if (value != null && reactive) {
3064
3061
  status.imports.add("t_attr");
3065
3062
  attributes.push(`${name}="${trimQuotes(value).replaceAll("{", "${t_attr(").replaceAll("}", ")}")}"`);
3066
- } else if (name === "class" && node.scopeStyles) {
3063
+ } else if (name === "class" && node.scopeStyles && !status.inHead) {
3067
3064
  value = value ? `"${trimQuotes(value)} torp-${status.styleHash}"` : `"torp-${status.styleHash}"`;
3068
3065
  attributes.push(`${name}="${trimQuotes(value).replaceAll("\"", "&quot;")}"`);
3069
3066
  needsClass = false;
@@ -3081,10 +3078,7 @@ function buildServerRootNode(node, status, b) {
3081
3078
  //#region src/compile/build/server/buildServerSlotNode.ts
3082
3079
  function buildServerSlotNode(node, status, b) {
3083
3080
  status.output += HYDRATION_START_COMMENT;
3084
- if (status.output) {
3085
- b.append(`t_body += \`${status.output}\`;`);
3086
- status.output = "";
3087
- }
3081
+ flushOutput(status, b);
3088
3082
  let slotName = node.attributes.find((a) => a.name === "name")?.value;
3089
3083
  slotName = slotName ? trimQuotes(slotName) : "_";
3090
3084
  const propsName = nextVarName("slot_props", status);
@@ -3096,15 +3090,12 @@ function buildServerSlotNode(node, status, b) {
3096
3090
  else b.append(`${propsName}["${name}"] = true;`);
3097
3091
  }
3098
3092
  b.append(`if ($slots && $slots["${slotName}"]) {`);
3099
- b.append(`t_body += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3093
+ b.append(`${status.inHead ? "t_head" : "t_body"} += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3100
3094
  const fill = node.children.find((c) => isSpecialNode(c) && (c.tagName === "fill" || c.tagName === "filldef"));
3101
3095
  if (fill && isSpecialNode(fill) && fill.children.length) {
3102
3096
  b.append(`} else {`);
3103
3097
  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
- }
3098
+ flushOutput(status, b);
3108
3099
  }
3109
3100
  b.append(`}`);
3110
3101
  status.output += HYDRATION_END_COMMENT;
@@ -3302,7 +3293,23 @@ function buildServerTemplate(template, imports, b, options) {
3302
3293
  }
3303
3294
  b.append("}");
3304
3295
  }
3305
- } else if (chunk.script === "/* @head */") {} else if (chunk.script === "/* @style */") {
3296
+ } else if (chunk.script === "/* @head */") {
3297
+ if (current.head) {
3298
+ const status = {
3299
+ imports,
3300
+ output: "",
3301
+ styleHash: current.style?.hash || "",
3302
+ varNames: {},
3303
+ preserveWhitespace: false,
3304
+ inHead: true,
3305
+ options
3306
+ };
3307
+ b.append("");
3308
+ b.append("/* Head */");
3309
+ buildServerNode(current.head, status, b);
3310
+ flushOutput(status, b);
3311
+ }
3312
+ } else if (chunk.script === "/* @style */") {
3306
3313
  if (current.style) {
3307
3314
  b.append("");
3308
3315
  b.append("/* Style */");
@@ -3531,9 +3538,10 @@ function parseInlineScript(status) {
3531
3538
  continue;
3532
3539
  }
3533
3540
  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;
3541
+ else if (accept("}", status)) {
3542
+ if (braceCount > 0) braceCount -= 1;
3543
+ else return status.source.substring(start, status.i - 1);
3544
+ } else status.i += 1;
3537
3545
  }
3538
3546
  return "";
3539
3547
  }
@@ -3889,9 +3897,10 @@ function usesSlot(nodes) {
3889
3897
  */
3890
3898
  function slottifyChildNodes(node) {
3891
3899
  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);
3900
+ for (let child of node.children) if (isFillNode(child)) {
3901
+ if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3902
+ else break;
3903
+ } else nonFillNodes.push(child);
3895
3904
  if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3896
3905
  if (nonFillNodes.length) {
3897
3906
  let defaultFillNode = node.children.find((n) => isFillNode(n) && !n.attributes.find((a) => a.name === "name"));
@@ -4377,11 +4386,12 @@ function hash(input) {
4377
4386
  * @returns True if the supplied string was found at the current parse position
4378
4387
  */
4379
4388
  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");
4389
+ if (status.i < status.source.length) {
4390
+ if (status.source.substring(status.i, status.i + value.length) == value) {
4391
+ status.i += advance ? value.length : 0;
4392
+ return true;
4393
+ } else addError(status, `Expected ${value}`);
4394
+ } else addError(status, "Expected token");
4385
4395
  return false;
4386
4396
  }
4387
4397
  //#endregion
@@ -4532,21 +4542,25 @@ function parseCode(source) {
4532
4542
  status.level = 0;
4533
4543
  }
4534
4544
  } else if (accept("function", status)) {
4535
- if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
4536
- else status.i--;
4545
+ if (consumeSpace(status)) {
4546
+ if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
4547
+ else status.i--;
4548
+ }
4537
4549
  } 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)) {
4550
+ if (consumeSpace(status)) {
4551
+ if (/[A-Z]/.test(source[status.i])) {
4552
+ const start = status.i;
4553
+ consumeAlphaNumeric(status);
4543
4554
  consumeSpace(status);
4544
- if (accept("(", status, false)) {
4545
- status.i = start;
4546
- parseComponentStart(status);
4555
+ if (accept("=", status)) {
4556
+ consumeSpace(status);
4557
+ if (accept("(", status, false)) {
4558
+ status.i = start;
4559
+ parseComponentStart(status);
4560
+ }
4547
4561
  }
4548
- }
4549
- } else status.i--;
4562
+ } else status.i--;
4563
+ }
4550
4564
  } else if (accept("export", status)) {
4551
4565
  const start = status.i;
4552
4566
  if (consumeSpace(status) && accept("default", status) && consumeSpace(status) && accept("(", status, false)) parseComponentStart(status);