@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.cjs CHANGED
@@ -538,9 +538,7 @@ function buildNodeFragmentText(node, status, fragments, currentFragment) {
538
538
  if (currentFragment) currentFragment.text += isReactive(content) ? "#" : content;
539
539
  break;
540
540
  }
541
- case "special":
542
- buildSpecialFragmentText(node, status, fragments, currentFragment);
543
- break;
541
+ case "special": buildSpecialFragmentText(node, status, fragments, currentFragment);
544
542
  }
545
543
  }
546
544
  function buildRootFragmentText(node, status, fragments) {
@@ -585,7 +583,6 @@ function buildControlFragmentText(node, status, fragments, currentFragment) {
585
583
  };
586
584
  fragments.push(node.fragment);
587
585
  for (let child of node.children) buildNodeFragmentText(child, status, fragments, node.fragment);
588
- break;
589
586
  }
590
587
  }
591
588
  function buildComponentFragmentText(node, status, fragments, currentFragment) {
@@ -935,9 +932,7 @@ function declareControlFragmentVars(fragment, node, path, status, b, parentName,
935
932
  case "@html group":
936
933
  declareParentAndAnchorFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, node.operation.substring(1).replace(" group", ""), declare);
937
934
  break;
938
- default:
939
- for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
940
- break;
935
+ default: for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, path, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
941
936
  }
942
937
  }
943
938
  function declareComponentFragmentVars(fragment, node, path, status, b, parentName, anchorName, varPaths, declare) {
@@ -1006,8 +1001,10 @@ function declareElementFragmentVars(fragment, node, path, status, b, parentName,
1006
1001
  else b.append(`t_elm("${node.tagName}", {${attributes}}, [`);
1007
1002
  }
1008
1003
  for (let [i, child] of node.children.entries()) declareFragmentVars(fragment, child, elementPath, status, b, parentName, anchorName, varPaths, i === node.children.length - 1, declare);
1009
- if (!declare) if (declareVariable || childParentName) b.append("])),");
1010
- else b.append("]),");
1004
+ if (!declare) {
1005
+ if (declareVariable || childParentName) b.append("])),");
1006
+ else b.append("]),");
1007
+ }
1011
1008
  }
1012
1009
  function elementNodeNeedsDeclaration(node, topLevel, lastChild) {
1013
1010
  const hasReactiveAttribute = node.attributes.some((a) => a.value && a.reactive);
@@ -1128,10 +1125,12 @@ function getFragmentVarPath(fragment, status, name, path, varPaths) {
1128
1125
  let level = 0;
1129
1126
  let end = 0;
1130
1127
  for (let i = pos + match.length; i < varPath.length; i++) if (varPath[i] === "(") level++;
1131
- else if (varPath[i] === ")") if (level === 0) {
1132
- end = i;
1133
- break;
1134
- } else level--;
1128
+ else if (varPath[i] === ")") {
1129
+ if (level === 0) {
1130
+ end = i;
1131
+ break;
1132
+ } else level--;
1133
+ }
1135
1134
  const tail = varPath.substring(end + count);
1136
1135
  varPath = `${head}t_skip(${varPath.substring(pos + match.length, end)}, ${count})${tail}`;
1137
1136
  }
@@ -1147,10 +1146,12 @@ function getFragmentVarPathPart(path, varPath, status, root = false, rootFn = "t
1147
1146
  varPath = `t_child(${varPath})`;
1148
1147
  }
1149
1148
  for (let [i, child] of path.children.entries()) {
1150
- if (i > 0) if (child.type === "text" || child.type === "space") varPath = `t_next_text(${varPath}, true)`;
1151
- else {
1152
- status.imports.add("t_next");
1153
- varPath = `t_next(${varPath})`;
1149
+ if (i > 0) {
1150
+ if (child.type === "text" || child.type === "space") varPath = `t_next_text(${varPath}, true)`;
1151
+ else {
1152
+ status.imports.add("t_next");
1153
+ varPath = `t_next(${varPath})`;
1154
+ }
1154
1155
  }
1155
1156
  if (i === path.children.length - 1 && child.children.length) varPath = getFragmentVarPathPart(child, varPath, status);
1156
1157
  }
@@ -1285,39 +1286,40 @@ function buildComponentNode(node, status, b, root = false) {
1285
1286
  let props = [];
1286
1287
  let runs = [];
1287
1288
  let bindingRuns = [];
1288
- for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@component") {} else if (value != null && fullyReactive) if (name.startsWith("&")) {
1289
- name = name.substring(1);
1290
- props.push({
1291
- name,
1292
- value,
1293
- spans: [span]
1294
- });
1295
- runs.push(`${propsName}["${name}"] = ${value};`);
1296
- bindingRuns.push(bindingTarget(value, `${propsName}["${name}"]`, name, status.imports));
1297
- } else if (name === "class") {
1298
- if (node.scopeStyles) value = `[${value}, "torp-${status.styleHash}"]`;
1299
- props.push({
1300
- name,
1301
- value,
1302
- spans: [span]
1303
- });
1304
- runs.push(`${propsName}["${name}"] = ${value};`);
1305
- } else if (name === "style") {
1306
- props.push({
1307
- name,
1308
- value,
1309
- spans: [span]
1310
- });
1311
- runs.push(`${propsName}["${name}"] = ${value};`);
1312
- } else {
1313
- props.push({
1314
- name,
1315
- value,
1316
- spans: [span]
1317
- });
1318
- runs.push(`${propsName}["${name}"] = ${value};`);
1319
- }
1320
- else if (value != null && reactive) {
1289
+ for (let { name, value, reactive, fullyReactive, span } of node.attributes) if (name === "self" && node.tagName === "@component") {} else if (value != null && fullyReactive) {
1290
+ if (name.startsWith("&")) {
1291
+ name = name.substring(1);
1292
+ props.push({
1293
+ name,
1294
+ value,
1295
+ spans: [span]
1296
+ });
1297
+ runs.push(`${propsName}["${name}"] = ${value};`);
1298
+ bindingRuns.push(bindingTarget(value, `${propsName}["${name}"]`, name, status.imports));
1299
+ } else if (name === "class") {
1300
+ if (node.scopeStyles) value = `[${value}, "torp-${status.styleHash}"]`;
1301
+ props.push({
1302
+ name,
1303
+ value,
1304
+ spans: [span]
1305
+ });
1306
+ runs.push(`${propsName}["${name}"] = ${value};`);
1307
+ } else if (name === "style") {
1308
+ props.push({
1309
+ name,
1310
+ value,
1311
+ spans: [span]
1312
+ });
1313
+ runs.push(`${propsName}["${name}"] = ${value};`);
1314
+ } else {
1315
+ props.push({
1316
+ name,
1317
+ value,
1318
+ spans: [span]
1319
+ });
1320
+ runs.push(`${propsName}["${name}"] = ${value};`);
1321
+ }
1322
+ } else if (value != null && reactive) {
1321
1323
  const { newValue, spans, offsets, lengths } = getAttributeOffsets(value, span);
1322
1324
  value = newValue;
1323
1325
  if (name === "class") {
@@ -1575,8 +1577,10 @@ function collectExpressionStrings(node, out) {
1575
1577
  }
1576
1578
  if (isElementLike(node)) {
1577
1579
  for (const attr of node.attributes) {
1578
- if (attr.value !== void 0) if (attr.name.startsWith("&")) out.push(`${attr.value} =`);
1579
- else out.push(attr.value);
1580
+ if (attr.value !== void 0) {
1581
+ if (attr.name.startsWith("&")) out.push(`${attr.value} =`);
1582
+ else out.push(attr.value);
1583
+ }
1580
1584
  if (attr.fullyReactive && attr.name !== attr.value) out.push(attr.name);
1581
1585
  }
1582
1586
  for (const child of node.children) collectExpressionStrings(child, out);
@@ -2159,10 +2163,11 @@ function buildTitleNode(node, status, b) {
2159
2163
  }
2160
2164
  function buildHeadNode(node, status, b) {
2161
2165
  status.imports.add("$run");
2166
+ status.imports.add("t_head_element");
2167
+ const selector = headElementSelector(node);
2162
2168
  b.append("$run(() => {");
2163
2169
  b.append(`
2164
- const t_head_el = document.createElement("${node.tagName}");
2165
- document.getElementsByTagName("head")[0].appendChild(t_head_el);`);
2170
+ const t_head_el = t_head_element("${node.tagName}", ${JSON.stringify(selector)});`);
2166
2171
  for (let { name, value, span } of node.attributes) if (value != null) {
2167
2172
  status.imports.add("t_attribute");
2168
2173
  value = replaceForVarNames(value, status);
@@ -2170,31 +2175,55 @@ function buildHeadNode(node, status, b) {
2170
2175
  }
2171
2176
  b.append(`}${status.options.dev === true ? `, "runHead"` : ""});`);
2172
2177
  }
2178
+ /**
2179
+ * Builds a selector that finds an equivalent element in the head, from the
2180
+ * attributes that identify it (e.g. `meta[name="description"]`). Returns an
2181
+ * empty string when the element has no identifying attributes, in which case
2182
+ * a new element is always created.
2183
+ */
2184
+ function headElementSelector(node) {
2185
+ const identityAttributes = [
2186
+ "name",
2187
+ "property",
2188
+ "rel",
2189
+ "charset",
2190
+ "http-equiv",
2191
+ "itemprop"
2192
+ ];
2193
+ const parts = [];
2194
+ for (let { name, value, reactive } of node.attributes) {
2195
+ if (!identityAttributes.includes(name) || value == null || reactive) continue;
2196
+ const attributeValue = trimQuotes(value).replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
2197
+ parts.push(`[${name}="${attributeValue}"]`);
2198
+ }
2199
+ return parts.length ? node.tagName + parts.join("") : "";
2200
+ }
2173
2201
  function buildElementAttributes$1(node, varName, status, b) {
2174
2202
  let fragment = status.fragmentStack.at(-1)?.fragment;
2175
2203
  if (!fragment) throw new Error("No fragment on stack");
2176
- 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);
2177
- else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b);
2178
- else if (name === "onmount") buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
2179
- else if (name.startsWith("on")) buildEventAttribute(varName, name, value, span, status, b);
2180
- else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
2181
- else if (name === "class") {
2182
- status.imports.add("t_class");
2183
- const params = [value];
2184
- if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
2185
- const propName = status.ns ? "className.baseVal" : "className";
2186
- stashRun(fragment, `${status.ns ? "// @ts-ignore\n" : ""}${varName}.${propName} = t_class(`, params.join(", "), ");", span, status);
2187
- } else if (name === "style") {
2188
- status.imports.add("t_style");
2189
- stashRun(fragment, `${varName}.style.cssText += t_style(`, value, ");", span, status);
2190
- } else if (name.includes("-")) {
2191
- status.imports.add("t_attribute");
2192
- stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2193
- } else {
2194
- status.imports.add("t_attribute");
2195
- stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2196
- }
2197
- else if (value != null && reactive) {
2204
+ 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) {
2205
+ if (name === "&group") buildBindGroupAttribute(node, varName, value, status, b);
2206
+ else if (name === "&value" || name === "&checked") buildBindAttribute(node, varName, name, value, status, b);
2207
+ else if (name === "onmount") buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
2208
+ else if (name.startsWith("on")) buildEventAttribute(varName, name, value, span, status, b);
2209
+ else if (name.startsWith("transition")) buildTransitionAttribute(node, varName, name, value, status, b);
2210
+ else if (name === "class") {
2211
+ status.imports.add("t_class");
2212
+ const params = [value];
2213
+ if (node.scopeStyles) params.push(`"torp-${status.styleHash}"`);
2214
+ const propName = status.ns ? "className.baseVal" : "className";
2215
+ stashRun(fragment, `${status.ns ? "// @ts-ignore\n" : ""}${varName}.${propName} = t_class(`, params.join(", "), ");", span, status);
2216
+ } else if (name === "style") {
2217
+ status.imports.add("t_style");
2218
+ stashRun(fragment, `${varName}.style.cssText += t_style(`, value, ");", span, status);
2219
+ } else if (name.includes("-")) {
2220
+ status.imports.add("t_attribute");
2221
+ stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2222
+ } else {
2223
+ status.imports.add("t_attribute");
2224
+ stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
2225
+ }
2226
+ } else if (value != null && reactive) {
2198
2227
  const { newValue, spans, offsets, lengths } = getAttributeOffsets(value, span);
2199
2228
  value = newValue;
2200
2229
  if (name === "class") {
@@ -2246,7 +2275,6 @@ function buildBindAttribute(node, varName, name, value, status, b) {
2246
2275
  case "radio":
2247
2276
  eventName = "change";
2248
2277
  inputValue = "e.target.value";
2249
- break;
2250
2278
  }
2251
2279
  } else if (node.tagName === "select") {
2252
2280
  eventName = "change";
@@ -2289,10 +2317,12 @@ function buildTransitionAttribute(node, varName, name, value, status, b) {
2289
2317
  b.append(`const ${entryVarName} = (${value})(${varName});`);
2290
2318
  b.append(`t_animate(${varName}, ${entryVarName});`);
2291
2319
  }
2292
- } else if (name === "transition-out") if (node.attributes.find((a) => a.name === "transition-in")) return;
2293
- else {
2294
- b.append(`const ${exitVarName} = (${value})(${varName});`);
2295
- b.append(`t_animate(${varName}, null, ${entryVarName});`);
2320
+ } else if (name === "transition-out") {
2321
+ if (node.attributes.find((a) => a.name === "transition-in")) return;
2322
+ else {
2323
+ b.append(`const ${exitVarName} = (${value})(${varName});`);
2324
+ b.append(`t_animate(${varName}, null, ${entryVarName});`);
2325
+ }
2296
2326
  }
2297
2327
  }
2298
2328
  //#endregion
@@ -2505,6 +2535,7 @@ const importsMap$1 = {
2505
2535
  t_attribute: "import { t_attribute } from \"${folder}\";",
2506
2536
  t_dynamic: "import { t_dynamic } from \"${folder}\";",
2507
2537
  t_fmt: "import { t_fmt } from \"${folder}\";",
2538
+ t_head_element: "import { t_head_element } from \"${folder}\";",
2508
2539
  t_fragment: "import { t_fragment } from \"${folder}\";",
2509
2540
  t_fragment_el: "import { t_fragment_el } from \"${folder}\";",
2510
2541
  t_event: "import { t_event } from \"${folder}\";",
@@ -2716,13 +2747,22 @@ function buildAttributeNode(attribute, b) {
2716
2747
  b.append(`${attribute.name}: ${attribute.value};`);
2717
2748
  }
2718
2749
  //#endregion
2719
- //#region src/compile/build/server/buildServerComponentNode.ts
2720
- function buildServerComponentNode(node, status, b) {
2721
- status.output += HYDRATION_START_COMMENT;
2750
+ //#region src/compile/build/server/flushOutput.ts
2751
+ /**
2752
+ * Appends any pending output to the generated code, targeting the body
2753
+ * string -- or the head string, when building inside a @head block.
2754
+ */
2755
+ function flushOutput(status, b) {
2722
2756
  if (status.output) {
2723
- b.append(`t_body += \`${status.output}\`;`);
2757
+ b.append(`${status.inHead ? "t_head" : "t_body"} += \`${status.output}\`;`);
2724
2758
  status.output = "";
2725
2759
  }
2760
+ }
2761
+ //#endregion
2762
+ //#region src/compile/build/server/buildServerComponentNode.ts
2763
+ function buildServerComponentNode(node, status, b) {
2764
+ status.output += HYDRATION_START_COMMENT;
2765
+ flushOutput(status, b);
2726
2766
  const componentHasProps = node.attributes.length;
2727
2767
  const propsName = componentHasProps ? nextVarName("props", status) : "undefined";
2728
2768
  if (componentHasProps) {
@@ -2780,7 +2820,7 @@ function buildServerComponentNode(node, status, b) {
2780
2820
  if (slotsName !== "undefined") renderParams += `, ${slotsName}`;
2781
2821
  const componentResult = nextVarName("comp", status);
2782
2822
  b.append(`const ${componentResult} = ${componentName}(${renderParams});`);
2783
- b.append(`t_body += ${componentResult}.body;`);
2823
+ b.append(`${status.inHead ? "t_head" : "t_body"} += ${componentResult}.body;`);
2784
2824
  b.append(`t_head += ${componentResult}.head;`);
2785
2825
  status.output += HYDRATION_END_COMMENT;
2786
2826
  status.output += "<!>";
@@ -2789,29 +2829,20 @@ function buildServerComponentNode(node, status, b) {
2789
2829
  //#region src/compile/build/server/buildServerAwaitNode.ts
2790
2830
  function buildServerAwaitNode(node, status, b) {
2791
2831
  status.output += HYDRATION_START_COMMENT;
2792
- if (status.output) {
2793
- b.append(`t_body += \`${status.output}\`;`);
2794
- status.output = "";
2795
- }
2832
+ flushOutput(status, b);
2796
2833
  for (let branch of node.children) if (isControlNode(branch) && branch.operation === "@with") buildServerAwaitBranch(branch, status, b);
2797
2834
  status.output += HYDRATION_END_COMMENT;
2798
2835
  status.output += "<!>";
2799
2836
  }
2800
2837
  function buildServerAwaitBranch(node, status, b) {
2801
2838
  for (let child of node.children) buildServerNode(child, status, b);
2802
- if (status.output) {
2803
- b.append(`t_body += \`${status.output}\`;`);
2804
- status.output = "";
2805
- }
2839
+ flushOutput(status, b);
2806
2840
  }
2807
2841
  //#endregion
2808
2842
  //#region src/compile/build/server/buildServerForNode.ts
2809
2843
  function buildServerForNode(node, status, b) {
2810
2844
  status.output += HYDRATION_START_COMMENT;
2811
- if (status.output) {
2812
- b.append(`t_body += \`${status.output}\`;`);
2813
- status.output = "";
2814
- }
2845
+ flushOutput(status, b);
2815
2846
  for (let branch of node.children) if (isControlNode(branch)) buildServerForBranch(branch, status, b);
2816
2847
  status.output += HYDRATION_END_COMMENT;
2817
2848
  status.output += "<!>";
@@ -2820,10 +2851,7 @@ function buildServerForBranch(node, status, b) {
2820
2851
  b.append(`${node.statement} {`);
2821
2852
  status.output += HYDRATION_BREAK_COMMENT;
2822
2853
  for (let child of node.children) buildServerNode(child, status, b);
2823
- if (status.output) {
2824
- b.append(`t_body += \`${status.output}\`;`);
2825
- status.output = "";
2826
- }
2854
+ flushOutput(status, b);
2827
2855
  b.append("}");
2828
2856
  }
2829
2857
  //#endregion
@@ -2838,10 +2866,7 @@ function buildServerHtmlNode(node, status) {
2838
2866
  //#region src/compile/build/server/buildServerIfNode.ts
2839
2867
  function buildServerIfNode(node, status, b) {
2840
2868
  status.output += HYDRATION_START_COMMENT;
2841
- if (status.output) {
2842
- b.append(`t_body += \`${status.output}\`;`);
2843
- status.output = "";
2844
- }
2869
+ flushOutput(status, b);
2845
2870
  for (let branch of node.children) if (isControlNode(branch)) buildServerIfBranch(branch, status, b);
2846
2871
  status.output += HYDRATION_END_COMMENT;
2847
2872
  status.output += "<!>";
@@ -2850,20 +2875,14 @@ function buildServerIfBranch(node, status, b) {
2850
2875
  b.append(`${node.statement} {`);
2851
2876
  status.output += HYDRATION_BREAK_COMMENT;
2852
2877
  for (let child of node.children) buildServerNode(child, status, b);
2853
- if (status.output) {
2854
- b.append(`t_body += \`${status.output}\`;`);
2855
- status.output = "";
2856
- }
2878
+ flushOutput(status, b);
2857
2879
  b.append("}");
2858
2880
  }
2859
2881
  //#endregion
2860
2882
  //#region src/compile/build/server/buildServerReplaceNode.ts
2861
2883
  function buildServerReplaceNode(node, status, b) {
2862
2884
  status.output += HYDRATION_START_COMMENT;
2863
- if (status.output) {
2864
- b.append(`t_body += \`${status.output}\`;`);
2865
- status.output = "";
2866
- }
2885
+ flushOutput(status, b);
2867
2886
  for (let branch of node.children) if (isControlNode(branch)) buildServerReplaceBranch(branch, status, b);
2868
2887
  status.output += HYDRATION_END_COMMENT;
2869
2888
  status.output += "<!>";
@@ -2871,10 +2890,7 @@ function buildServerReplaceNode(node, status, b) {
2871
2890
  function buildServerReplaceBranch(node, status, b) {
2872
2891
  b.append(`${node.statement};`);
2873
2892
  for (let child of node.children) buildServerNode(child, status, b);
2874
- if (status.output) {
2875
- b.append(`t_body += \`${status.output}\`;`);
2876
- status.output = "";
2877
- }
2893
+ flushOutput(status, b);
2878
2894
  }
2879
2895
  //#endregion
2880
2896
  //#region src/compile/build/server/buildServerScriptNode.ts
@@ -2889,10 +2905,7 @@ function maybeAppend(text, end) {
2889
2905
  //#region src/compile/build/server/buildServerSwitchNode.ts
2890
2906
  function buildServerSwitchNode(node, status, b) {
2891
2907
  status.output += HYDRATION_START_COMMENT;
2892
- if (status.output) {
2893
- b.append(`t_body += \`${status.output}\`;`);
2894
- status.output = "";
2895
- }
2908
+ flushOutput(status, b);
2896
2909
  b.append(`${node.statement} {`);
2897
2910
  for (let branch of node.children.filter((c) => isControlNode(c))) if (isControlNode(branch)) buildServerSwitchBranch(branch, status, b);
2898
2911
  b.append("}");
@@ -2903,10 +2916,7 @@ function buildServerSwitchBranch(node, status, b) {
2903
2916
  b.append(`${node.statement} {`);
2904
2917
  status.output += HYDRATION_BREAK_COMMENT;
2905
2918
  for (let child of node.children) buildServerNode(child, status, b);
2906
- if (status.output) {
2907
- b.append(`t_body += \`${status.output}\`;`);
2908
- status.output = "";
2909
- }
2919
+ flushOutput(status, b);
2910
2920
  b.append("break;");
2911
2921
  b.append("}");
2912
2922
  }
@@ -2914,10 +2924,7 @@ function buildServerSwitchBranch(node, status, b) {
2914
2924
  //#region src/compile/build/server/buildServerTryNode.ts
2915
2925
  function buildServerTryNode(node, status, b) {
2916
2926
  status.output += HYDRATION_START_COMMENT;
2917
- if (status.output) {
2918
- b.append(`t_body += \`${status.output}\`;`);
2919
- status.output = "";
2920
- }
2927
+ flushOutput(status, b);
2921
2928
  const branches = node.children.filter((n) => isControlNode(n));
2922
2929
  let tryBranch = branches.find((n) => n.operation === "@try");
2923
2930
  let catchBranch = branches.find((n) => n.operation === "@catch");
@@ -2938,10 +2945,7 @@ function buildServerTryNode(node, status, b) {
2938
2945
  }
2939
2946
  function buildServerTryBranch(node, status, b) {
2940
2947
  for (let child of node.children) buildServerNode(child, status, b);
2941
- if (status.output) {
2942
- b.append(`t_body += \`${status.output}\`;`);
2943
- status.output = "";
2944
- }
2948
+ flushOutput(status, b);
2945
2949
  }
2946
2950
  //#endregion
2947
2951
  //#region src/compile/build/server/buildServerControlNode.ts
@@ -2982,18 +2986,12 @@ function buildServerControlNode(node, status, b) {
2982
2986
  break;
2983
2987
  case "@html": break;
2984
2988
  case "@const":
2985
- if (status.output) {
2986
- b.append(`t_body += \`${status.output}\`;`);
2987
- status.output = "";
2988
- }
2989
+ flushOutput(status, b);
2989
2990
  buildServerScriptNode(node, b);
2990
2991
  break;
2991
2992
  case "@function":
2992
2993
  case "@async function":
2993
- if (status.output) {
2994
- b.append(`t_body += \`${status.output}\`;`);
2995
- status.output = "";
2996
- }
2994
+ flushOutput(status, b);
2997
2995
  b.append("");
2998
2996
  buildServerScriptNode(node, b);
2999
2997
  b.append("");
@@ -3023,48 +3021,47 @@ function buildServerElementNode(node, status, b) {
3023
3021
  }
3024
3022
  }
3025
3023
  function buildElementAttributes(node, status) {
3026
- let needsClass = node.scopeStyles;
3024
+ let needsClass = node.scopeStyles && !status.inHead;
3027
3025
  let attributes = [];
3028
- 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") {
3029
- if (name === "&value" && node.tagName === "select" && node.attributes.some((a) => a.name === "multiple")) continue;
3030
- let defaultValue = "";
3031
- let typeAttribute = node.attributes.find((a) => a.name === "type");
3032
- if (typeAttribute && typeAttribute.value) switch (trimQuotes(typeAttribute.value)) {
3033
- case "number":
3034
- defaultValue = "0";
3035
- break;
3036
- case "checkbox":
3037
- defaultValue = "false";
3038
- break;
3039
- }
3040
- if (!defaultValue) {
3026
+ 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) {
3027
+ 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": defaultValue = "false";
3036
+ }
3037
+ if (!defaultValue) {
3038
+ status.imports.add("t_attr");
3039
+ value = `t_attr(${value})`;
3040
+ defaultValue = "\"\"";
3041
+ }
3042
+ let valueOrDefault = `${value} || ${defaultValue}`;
3043
+ const propName = name.substring(1);
3044
+ attributes.push(`${propName}="\${${valueOrDefault}}"`);
3045
+ } else if (name === "class") {
3046
+ status.imports.add("t_class");
3047
+ let params = [value];
3048
+ if (node.scopeStyles && !status.inHead) {
3049
+ params.push(`"torp-${status.styleHash}"`);
3050
+ needsClass = false;
3051
+ }
3052
+ value = params.join(", ");
3053
+ attributes.push(`\${t_class(${value}) !== "" ? \`class="\${t_class(${value})}"\` : ""}`);
3054
+ } else if (name === "style") {
3055
+ status.imports.add("t_style");
3056
+ attributes.push(`\${t_style(${value}) !== "" ? \`style="\${t_style(${value})}"\` : ""}`);
3057
+ } else {
3041
3058
  status.imports.add("t_attr");
3042
- value = `t_attr(${value})`;
3043
- defaultValue = "\"\"";
3044
- }
3045
- let valueOrDefault = `${value} || ${defaultValue}`;
3046
- const propName = name.substring(1);
3047
- attributes.push(`${propName}="\${${valueOrDefault}}"`);
3048
- } else if (name === "class") {
3049
- status.imports.add("t_class");
3050
- let params = [value];
3051
- if (node.scopeStyles) {
3052
- params.push(`"torp-${status.styleHash}"`);
3053
- needsClass = false;
3059
+ attributes.push(`\${${value} ? \`${name}="\${t_attr(${value})}"\` : ""}`);
3054
3060
  }
3055
- value = params.join(", ");
3056
- attributes.push(`\${t_class(${value}) !== "" ? \`class="\${t_class(${value})}"\` : ""}`);
3057
- } else if (name === "style") {
3058
- status.imports.add("t_style");
3059
- attributes.push(`\${t_style(${value}) !== "" ? \`style="\${t_style(${value})}"\` : ""}`);
3060
- } else {
3061
- status.imports.add("t_attr");
3062
- attributes.push(`\${${value} ? \`${name}="\${t_attr(${value})}"\` : ""}`);
3063
- }
3064
- else if (value != null && reactive) {
3061
+ } else if (value != null && reactive) {
3065
3062
  status.imports.add("t_attr");
3066
3063
  attributes.push(`${name}="${trimQuotes(value).replaceAll("{", "${t_attr(").replaceAll("}", ")}")}"`);
3067
- } else if (name === "class" && node.scopeStyles) {
3064
+ } else if (name === "class" && node.scopeStyles && !status.inHead) {
3068
3065
  value = value ? `"${trimQuotes(value)} torp-${status.styleHash}"` : `"torp-${status.styleHash}"`;
3069
3066
  attributes.push(`${name}="${trimQuotes(value).replaceAll("\"", "&quot;")}"`);
3070
3067
  needsClass = false;
@@ -3082,10 +3079,7 @@ function buildServerRootNode(node, status, b) {
3082
3079
  //#region src/compile/build/server/buildServerSlotNode.ts
3083
3080
  function buildServerSlotNode(node, status, b) {
3084
3081
  status.output += HYDRATION_START_COMMENT;
3085
- if (status.output) {
3086
- b.append(`t_body += \`${status.output}\`;`);
3087
- status.output = "";
3088
- }
3082
+ flushOutput(status, b);
3089
3083
  let slotName = node.attributes.find((a) => a.name === "name")?.value;
3090
3084
  slotName = slotName ? trimQuotes(slotName) : "_";
3091
3085
  const propsName = nextVarName("slot_props", status);
@@ -3097,15 +3091,12 @@ function buildServerSlotNode(node, status, b) {
3097
3091
  else b.append(`${propsName}["${name}"] = true;`);
3098
3092
  }
3099
3093
  b.append(`if ($slots && $slots["${slotName}"]) {`);
3100
- b.append(`t_body += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3094
+ b.append(`${status.inHead ? "t_head" : "t_body"} += $slots["${slotName}"](${slotHasProps ? propsName : "undefined"}, $context);`);
3101
3095
  const fill = node.children.find((c) => isSpecialNode(c) && (c.tagName === "fill" || c.tagName === "filldef"));
3102
3096
  if (fill && isSpecialNode(fill) && fill.children.length) {
3103
3097
  b.append(`} else {`);
3104
3098
  for (let child of fill.children) buildServerNode(child, status, b);
3105
- if (status.output) {
3106
- b.append(`t_body += \`${status.output}\`;`);
3107
- status.output = "";
3108
- }
3099
+ flushOutput(status, b);
3109
3100
  }
3110
3101
  b.append(`}`);
3111
3102
  status.output += HYDRATION_END_COMMENT;
@@ -3303,7 +3294,23 @@ function buildServerTemplate(template, imports, b, options) {
3303
3294
  }
3304
3295
  b.append("}");
3305
3296
  }
3306
- } else if (chunk.script === "/* @head */") {} else if (chunk.script === "/* @style */") {
3297
+ } else if (chunk.script === "/* @head */") {
3298
+ if (current.head) {
3299
+ const status = {
3300
+ imports,
3301
+ output: "",
3302
+ styleHash: current.style?.hash || "",
3303
+ varNames: {},
3304
+ preserveWhitespace: false,
3305
+ inHead: true,
3306
+ options
3307
+ };
3308
+ b.append("");
3309
+ b.append("/* Head */");
3310
+ buildServerNode(current.head, status, b);
3311
+ flushOutput(status, b);
3312
+ }
3313
+ } else if (chunk.script === "/* @style */") {
3307
3314
  if (current.style) {
3308
3315
  b.append("");
3309
3316
  b.append("/* Style */");
@@ -3532,9 +3539,10 @@ function parseInlineScript(status) {
3532
3539
  continue;
3533
3540
  }
3534
3541
  if (accept("{", status)) braceCount += 1;
3535
- else if (accept("}", status)) if (braceCount > 0) braceCount -= 1;
3536
- else return status.source.substring(start, status.i - 1);
3537
- else status.i += 1;
3542
+ else if (accept("}", status)) {
3543
+ if (braceCount > 0) braceCount -= 1;
3544
+ else return status.source.substring(start, status.i - 1);
3545
+ } else status.i += 1;
3538
3546
  }
3539
3547
  return "";
3540
3548
  }
@@ -3890,9 +3898,10 @@ function usesSlot(nodes) {
3890
3898
  */
3891
3899
  function slottifyChildNodes(node) {
3892
3900
  let nonFillNodes = [];
3893
- for (let child of node.children) if (isFillNode(child)) if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3894
- else break;
3895
- else nonFillNodes.push(child);
3901
+ for (let child of node.children) if (isFillNode(child)) {
3902
+ if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3903
+ else break;
3904
+ } else nonFillNodes.push(child);
3896
3905
  if (nonFillNodes.find((n) => !isSpaceNode(n)) === void 0) nonFillNodes = [];
3897
3906
  if (nonFillNodes.length) {
3898
3907
  let defaultFillNode = node.children.find((n) => isFillNode(n) && !n.attributes.find((a) => a.name === "name"));
@@ -4378,11 +4387,12 @@ function hash(input) {
4378
4387
  * @returns True if the supplied string was found at the current parse position
4379
4388
  */
4380
4389
  function expect(value, status, advance = true) {
4381
- if (status.i < status.source.length) if (status.source.substring(status.i, status.i + value.length) == value) {
4382
- status.i += advance ? value.length : 0;
4383
- return true;
4384
- } else addError(status, `Expected ${value}`);
4385
- else addError(status, "Expected token");
4390
+ if (status.i < status.source.length) {
4391
+ if (status.source.substring(status.i, status.i + value.length) == value) {
4392
+ status.i += advance ? value.length : 0;
4393
+ return true;
4394
+ } else addError(status, `Expected ${value}`);
4395
+ } else addError(status, "Expected token");
4386
4396
  return false;
4387
4397
  }
4388
4398
  //#endregion
@@ -4533,21 +4543,25 @@ function parseCode(source) {
4533
4543
  status.level = 0;
4534
4544
  }
4535
4545
  } else if (accept("function", status)) {
4536
- if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
4537
- else status.i--;
4546
+ if (consumeSpace(status)) {
4547
+ if (/[A-Z]/.test(source[status.i])) parseComponentStart(status);
4548
+ else status.i--;
4549
+ }
4538
4550
  } else if (accept("const", status)) {
4539
- if (consumeSpace(status)) if (/[A-Z]/.test(source[status.i])) {
4540
- const start = status.i;
4541
- consumeAlphaNumeric(status);
4542
- consumeSpace(status);
4543
- if (accept("=", status)) {
4551
+ if (consumeSpace(status)) {
4552
+ if (/[A-Z]/.test(source[status.i])) {
4553
+ const start = status.i;
4554
+ consumeAlphaNumeric(status);
4544
4555
  consumeSpace(status);
4545
- if (accept("(", status, false)) {
4546
- status.i = start;
4547
- parseComponentStart(status);
4556
+ if (accept("=", status)) {
4557
+ consumeSpace(status);
4558
+ if (accept("(", status, false)) {
4559
+ status.i = start;
4560
+ parseComponentStart(status);
4561
+ }
4548
4562
  }
4549
- }
4550
- } else status.i--;
4563
+ } else status.i--;
4564
+ }
4551
4565
  } else if (accept("export", status)) {
4552
4566
  const start = status.i;
4553
4567
  if (consumeSpace(status) && accept("default", status) && consumeSpace(status) && accept("(", status, false)) parseComponentStart(status);