@barefootjs/go-template 0.1.2 → 0.1.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/build.js CHANGED
@@ -423,6 +423,7 @@ class GoTemplateAdapter extends BaseAdapter {
423
423
  errors = [];
424
424
  propsObjectName = null;
425
425
  restPropsName = null;
426
+ templateVarCounter = 0;
426
427
  localTypeNames = new Set;
427
428
  localTypeAliases = new Map;
428
429
  usesHtmlTemplate = false;
@@ -437,6 +438,7 @@ class GoTemplateAdapter extends BaseAdapter {
437
438
  generate(ir, options) {
438
439
  this.componentName = ir.metadata.componentName;
439
440
  this.errors = [];
441
+ this.templateVarCounter = 0;
440
442
  this.propsObjectName = ir.metadata.propsObjectName;
441
443
  this.restPropsName = ir.metadata.restPropsName ?? null;
442
444
  if (!options?.siblingTemplatesRegistered) {
@@ -776,7 +778,7 @@ ${goFields.join(`
776
778
  lines.push("\tScopeID string // Optional: if empty, random ID is generated");
777
779
  lines.push("\tBfParent string // Optional: parent scope id");
778
780
  lines.push("\tBfMount string // Optional: slot id in parent");
779
- const staticNested = nestedComponents.filter((n) => !n.isDynamic);
781
+ const inputNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
780
782
  const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
781
783
  for (const param of ir.metadata.propsParams) {
782
784
  const fieldName = this.capitalizeFieldName(param.name);
@@ -785,7 +787,7 @@ ${goFields.join(`
785
787
  const goType = propTypeOverrides.get(param.name) ?? this.typeInfoToGo(param.type, param.defaultValue);
786
788
  lines.push(` ${fieldName} ${goType}`);
787
789
  }
788
- for (const nested of staticNested) {
790
+ for (const nested of inputNested) {
789
791
  lines.push(` ${nested.name}s []${nested.name}Input`);
790
792
  }
791
793
  const restPropsName = ir.metadata.restPropsName;
@@ -861,7 +863,7 @@ ${goFields.join(`
861
863
  lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
862
864
  }
863
865
  for (const nested of nestedComponents) {
864
- if (nested.isDynamic) {
866
+ if (nested.isDynamic && !nested.isPropDerived) {
865
867
  lines.push(` ${nested.name}s []${nested.name}Props \`json:"-"\``);
866
868
  } else {
867
869
  const jsonTag = this.toJsonTag(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`);
@@ -882,9 +884,9 @@ ${goFields.join(`
882
884
  generateNewPropsFunction(lines, ir, componentName, nestedComponents, spreadSlots) {
883
885
  const inputTypeName = `${componentName}Input`;
884
886
  const propsTypeName = `${componentName}Props`;
885
- const dynamicNested = nestedComponents.filter((n) => n.isDynamic);
887
+ const signalDynamicNested = nestedComponents.filter((n) => n.isDynamic && !n.isPropDerived);
886
888
  lines.push(`// New${componentName}Props creates ${propsTypeName} from ${inputTypeName}.`);
887
- for (const nested of dynamicNested) {
889
+ for (const nested of signalDynamicNested) {
888
890
  const arrayField = `${nested.name}s`;
889
891
  lines.push(`//`);
890
892
  lines.push(`// NOTE: \`${arrayField}\` is populated by the route handler, not by`);
@@ -906,7 +908,7 @@ ${goFields.join(`
906
908
  lines.push(` scopeID = "${componentName}_" + randomID(6)`);
907
909
  lines.push("\t}");
908
910
  lines.push("");
909
- const staticNested = nestedComponents.filter((n) => !n.isDynamic);
911
+ const staticNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
910
912
  if (staticNested.length > 0) {
911
913
  for (const nested of staticNested) {
912
914
  const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
@@ -1054,7 +1056,8 @@ ${goFields.join(`
1054
1056
  if (!result.some((c) => c.name === loop.childComponent.name)) {
1055
1057
  result.push({
1056
1058
  ...loop.childComponent,
1057
- isDynamic: !loop.isStaticArray
1059
+ isDynamic: !loop.isStaticArray,
1060
+ isPropDerived: !!loop.isPropDerivedArray
1058
1061
  });
1059
1062
  }
1060
1063
  }
@@ -1877,7 +1880,7 @@ ${goFields.join(`
1877
1880
  if (literalType === "string")
1878
1881
  return `"${value}"`;
1879
1882
  if (literalType === "null")
1880
- return '""';
1883
+ return "nil";
1881
1884
  return String(value);
1882
1885
  }
1883
1886
  call(callee, args, emit) {
@@ -1913,7 +1916,7 @@ ${goFields.join(`
1913
1916
  if (result)
1914
1917
  return result;
1915
1918
  }
1916
- if (object.kind === "higher-order" && object.method === "find") {
1919
+ if (object.kind === "higher-order" && (object.method === "find" || object.method === "findLast")) {
1917
1920
  const findResult = this.renderHigherOrderExpr(object, emit);
1918
1921
  if (findResult) {
1919
1922
  return `{{with ${findResult}}}{{.${this.capitalizeFieldName(property)}}}{{end}}`;
@@ -2017,7 +2020,7 @@ ${goFields.join(`
2017
2020
  const result = this.renderHigherOrderExpr(reconstructed, emit);
2018
2021
  if (result)
2019
2022
  return result;
2020
- if (method === "find" || method === "findIndex") {
2023
+ if (method === "find" || method === "findIndex" || method === "findLast" || method === "findLastIndex") {
2021
2024
  const templateBlock = this.renderFindTemplateBlock(reconstructed, emit);
2022
2025
  if (templateBlock)
2023
2026
  return templateBlock;
@@ -2157,12 +2160,17 @@ ${goFields.join(`
2157
2160
  const value = negated ? "false" : "true";
2158
2161
  return `bf_filter ${arrayExpr} "${field}" ${value}`;
2159
2162
  }
2160
- if (expr.method === "find" || expr.method === "findIndex") {
2163
+ if (expr.method === "find" || expr.method === "findIndex" || expr.method === "findLast" || expr.method === "findLastIndex") {
2161
2164
  const eqPred = this.extractEqualityPredicate(expr.predicate, expr.param, (e) => this.renderParsedExpr(e));
2162
2165
  if (!eqPred)
2163
2166
  return null;
2164
- const func = expr.method === "find" ? "bf_find" : "bf_find_index";
2165
- return `${func} ${arrayExpr} "${eqPred.field}" ${eqPred.value}`;
2167
+ const funcMap = {
2168
+ find: "bf_find",
2169
+ findIndex: "bf_find_index",
2170
+ findLast: "bf_find_last",
2171
+ findLastIndex: "bf_find_last_index"
2172
+ };
2173
+ return `${funcMap[expr.method]} ${arrayExpr} "${eqPred.field}" ${eqPred.value}`;
2166
2174
  }
2167
2175
  return null;
2168
2176
  }
@@ -2178,6 +2186,15 @@ ${goFields.join(`
2178
2186
  if (expr.method === "findIndex") {
2179
2187
  return `{{range $i, $_ := ${arrayExpr}}}{{if ${condition}}}{{$i}}{{break}}{{end}}{{end}}`;
2180
2188
  }
2189
+ if (expr.method === "findLast") {
2190
+ const v = `$bf_r${this.templateVarCounter++}`;
2191
+ const capture = propertyAccess ? `.${propertyAccess}` : ".";
2192
+ return `{{${v} := ""}}{{range ${arrayExpr}}}{{if ${condition}}}{{${v} = ${capture}}}{{end}}{{end}}{{${v}}}`;
2193
+ }
2194
+ if (expr.method === "findLastIndex") {
2195
+ const v = `$bf_r${this.templateVarCounter++}`;
2196
+ return `{{${v} := -1}}{{range $i, $_ := ${arrayExpr}}}{{if ${condition}}}{{${v} = $i}}{{end}}{{end}}{{${v}}}`;
2197
+ }
2181
2198
  return null;
2182
2199
  }
2183
2200
  renderEverySomeTemplateBlock(expr, renderArray) {
@@ -2186,11 +2203,13 @@ ${goFields.join(`
2186
2203
  if (condition.includes("[UNSUPPORTED"))
2187
2204
  return null;
2188
2205
  if (expr.method === "every") {
2206
+ const v = `$bf_r${this.templateVarCounter++}`;
2189
2207
  const negated = this.negateGoCondition(condition);
2190
- return `{{$bf_result := true}}{{range ${arrayExpr}}}{{if ${negated}}}{{$bf_result = false}}{{break}}{{end}}{{end}}{{$bf_result}}`;
2208
+ return `{{${v} := true}}{{range ${arrayExpr}}}{{if ${negated}}}{{${v} = false}}{{break}}{{end}}{{end}}{{${v}}}`;
2191
2209
  }
2192
2210
  if (expr.method === "some") {
2193
- return `{{$bf_result := false}}{{range ${arrayExpr}}}{{if ${condition}}}{{$bf_result = true}}{{break}}{{end}}{{end}}{{$bf_result}}`;
2211
+ const v = `$bf_r${this.templateVarCounter++}`;
2212
+ return `{{${v} := false}}{{range ${arrayExpr}}}{{if ${condition}}}{{${v} = true}}{{break}}{{end}}{{end}}{{${v}}}`;
2194
2213
  }
2195
2214
  return null;
2196
2215
  }
@@ -2219,6 +2238,22 @@ ${goFields.join(`
2219
2238
  needsParens(expr) {
2220
2239
  return expr.kind === "logical" || expr.kind === "unary" || expr.kind === "conditional";
2221
2240
  }
2241
+ splitPreamble(rendered) {
2242
+ if (!rendered.includes("{{"))
2243
+ return null;
2244
+ const lastOpen = rendered.lastIndexOf("{{");
2245
+ const lastClose = rendered.lastIndexOf("}}");
2246
+ if (lastOpen >= 0 && lastClose > lastOpen) {
2247
+ const candidate = rendered.substring(lastOpen + 2, lastClose);
2248
+ if (!candidate.startsWith("$"))
2249
+ return null;
2250
+ return {
2251
+ preamble: rendered.substring(0, lastOpen),
2252
+ expr: candidate
2253
+ };
2254
+ }
2255
+ return null;
2256
+ }
2222
2257
  renderBlockBodyCondition(statements, param) {
2223
2258
  const localVarMap = new Map;
2224
2259
  const paths = this.collectReturnPaths(statements, [], localVarMap, param);
@@ -2339,7 +2374,7 @@ ${goFields.join(`
2339
2374
  return `"${expr.value}"`;
2340
2375
  }
2341
2376
  if (expr.literalType === "null") {
2342
- return '""';
2377
+ return "nil";
2343
2378
  }
2344
2379
  return String(expr.value);
2345
2380
  case "member": {
@@ -2616,138 +2651,138 @@ Options:
2616
2651
  });
2617
2652
  return { condition: `false`, preamble: "" };
2618
2653
  }
2619
- const rendered = this.renderConditionExpr(parsed);
2620
- if (rendered.startsWith("{{")) {
2621
- const lastOpen = rendered.lastIndexOf("{{");
2622
- const lastClose = rendered.lastIndexOf("}}");
2623
- if (lastOpen >= 0 && lastClose > lastOpen) {
2624
- const preamble = rendered.substring(0, lastOpen);
2625
- const condition = rendered.substring(lastOpen + 2, lastClose);
2626
- return { condition, preamble };
2627
- }
2628
- }
2629
- return { condition: rendered, preamble: "" };
2654
+ const { preamble, expr: condition } = this.renderConditionExpr(parsed);
2655
+ return { condition, preamble };
2630
2656
  }
2631
2657
  renderConditionExpr(expr) {
2658
+ const plain = (e) => ({ preamble: "", expr: e });
2632
2659
  switch (expr.kind) {
2633
2660
  case "identifier":
2634
2661
  {
2635
2662
  const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
2636
2663
  if (currentLoopParam && expr.name === currentLoopParam) {
2637
- return ".";
2664
+ return plain(".");
2638
2665
  }
2639
2666
  }
2640
- return `.${this.capitalizeFieldName(expr.name)}`;
2667
+ return plain(`.${this.capitalizeFieldName(expr.name)}`);
2641
2668
  case "literal":
2642
- if (expr.literalType === "string") {
2643
- return `"${expr.value}"`;
2644
- }
2645
- if (expr.literalType === "null") {
2646
- return '""';
2647
- }
2648
- return String(expr.value);
2669
+ if (expr.literalType === "string")
2670
+ return plain(`"${expr.value}"`);
2671
+ if (expr.literalType === "null")
2672
+ return plain("nil");
2673
+ return plain(String(expr.value));
2649
2674
  case "call": {
2650
2675
  if (expr.callee.kind === "identifier" && expr.args.length === 0) {
2651
- return `.${this.capitalizeFieldName(expr.callee.name)}`;
2676
+ return plain(`.${this.capitalizeFieldName(expr.callee.name)}`);
2652
2677
  }
2653
- return this.renderParsedExpr(expr);
2678
+ return plain(this.renderParsedExpr(expr));
2654
2679
  }
2655
2680
  case "member": {
2656
2681
  if (expr.property === "length" && expr.object.kind === "higher-order") {
2657
- const result = this.renderFilterLengthExpr(expr.object, (e) => this.renderConditionExpr(e));
2658
- if (result) {
2659
- return result;
2660
- }
2682
+ const result = this.renderFilterLengthExpr(expr.object, (e) => this.renderConditionExpr(e).expr);
2683
+ if (result)
2684
+ return plain(result);
2661
2685
  }
2662
2686
  if (expr.object.kind === "identifier" && this.propsObjectName && expr.object.name === this.propsObjectName) {
2663
- return `.${this.capitalizeFieldName(expr.property)}`;
2687
+ return plain(`.${this.capitalizeFieldName(expr.property)}`);
2664
2688
  }
2665
2689
  {
2666
2690
  const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
2667
2691
  if (expr.object.kind === "identifier" && currentLoopParam && expr.object.name === currentLoopParam) {
2668
- return `.${this.capitalizeFieldName(expr.property)}`;
2692
+ return plain(`.${this.capitalizeFieldName(expr.property)}`);
2669
2693
  }
2670
2694
  }
2671
2695
  const obj = this.renderConditionExpr(expr.object);
2672
2696
  if (expr.property === "length") {
2673
- return `len ${obj}`;
2697
+ return { preamble: obj.preamble, expr: `len ${obj.expr}` };
2674
2698
  }
2675
- return `${obj}.${this.capitalizeFieldName(expr.property)}`;
2699
+ return { preamble: obj.preamble, expr: `${obj.expr}.${this.capitalizeFieldName(expr.property)}` };
2676
2700
  }
2677
2701
  case "binary": {
2678
2702
  const leftNeedsParens = this.needsParensInGoTemplate(expr.left);
2679
- let left = this.renderConditionExpr(expr.left);
2680
- if (leftNeedsParens) {
2681
- left = `(${left})`;
2682
- }
2703
+ const leftResult = this.renderConditionExpr(expr.left);
2704
+ const left = leftNeedsParens ? `(${leftResult.expr})` : leftResult.expr;
2683
2705
  const rightNeedsParens = this.needsParensInGoTemplate(expr.right);
2684
- let right = this.renderConditionExpr(expr.right);
2685
- if (rightNeedsParens) {
2686
- right = `(${right})`;
2687
- }
2706
+ const rightResult = this.renderConditionExpr(expr.right);
2707
+ const right = rightNeedsParens ? `(${rightResult.expr})` : rightResult.expr;
2708
+ const preamble = leftResult.preamble + rightResult.preamble;
2709
+ let result;
2688
2710
  switch (expr.op) {
2689
2711
  case "===":
2690
2712
  case "==":
2691
- return `eq ${left} ${right}`;
2713
+ result = `eq ${left} ${right}`;
2714
+ break;
2692
2715
  case "!==":
2693
2716
  case "!=":
2694
- return `ne ${left} ${right}`;
2717
+ result = `ne ${left} ${right}`;
2718
+ break;
2695
2719
  case ">":
2696
- return `gt ${left} ${right}`;
2720
+ result = `gt ${left} ${right}`;
2721
+ break;
2697
2722
  case "<":
2698
- return `lt ${left} ${right}`;
2723
+ result = `lt ${left} ${right}`;
2724
+ break;
2699
2725
  case ">=":
2700
- return `ge ${left} ${right}`;
2726
+ result = `ge ${left} ${right}`;
2727
+ break;
2701
2728
  case "<=":
2702
- return `le ${left} ${right}`;
2729
+ result = `le ${left} ${right}`;
2730
+ break;
2703
2731
  case "+":
2704
- return `bf_add ${left} ${right}`;
2732
+ result = `bf_add ${left} ${right}`;
2733
+ break;
2705
2734
  case "-":
2706
- return `bf_sub ${left} ${right}`;
2735
+ result = `bf_sub ${left} ${right}`;
2736
+ break;
2707
2737
  case "*":
2708
- return `bf_mul ${left} ${right}`;
2738
+ result = `bf_mul ${left} ${right}`;
2739
+ break;
2709
2740
  case "/":
2710
- return `bf_div ${left} ${right}`;
2741
+ result = `bf_div ${left} ${right}`;
2742
+ break;
2711
2743
  default:
2712
- return `${left} ${expr.op} ${right}`;
2744
+ result = `${left} ${expr.op} ${right}`;
2713
2745
  }
2746
+ return { preamble, expr: result };
2714
2747
  }
2715
2748
  case "unary": {
2716
2749
  const arg = this.renderConditionExpr(expr.argument);
2717
- if (expr.op === "!") {
2718
- return `not ${arg}`;
2719
- }
2720
- if (expr.op === "-") {
2721
- return `bf_neg ${arg}`;
2722
- }
2750
+ if (expr.op === "!")
2751
+ return { preamble: arg.preamble, expr: `not ${arg.expr}` };
2752
+ if (expr.op === "-")
2753
+ return { preamble: arg.preamble, expr: `bf_neg ${arg.expr}` };
2723
2754
  return arg;
2724
2755
  }
2725
2756
  case "logical": {
2726
- const left = this.renderConditionExpr(expr.left);
2727
- const right = this.renderConditionExpr(expr.right);
2728
- const wrapLeft = this.needsParens(expr.left) ? `(${left})` : left;
2729
- const wrapRight = this.needsParens(expr.right) ? `(${right})` : right;
2730
- if (expr.op === "&&") {
2731
- return `and ${wrapLeft} ${wrapRight}`;
2732
- }
2733
- return `or ${wrapLeft} ${wrapRight}`;
2757
+ const leftResult = this.renderConditionExpr(expr.left);
2758
+ const rightResult = this.renderConditionExpr(expr.right);
2759
+ const preamble = leftResult.preamble + rightResult.preamble;
2760
+ const wrapLeft = this.needsParens(expr.left) ? `(${leftResult.expr})` : leftResult.expr;
2761
+ const wrapRight = this.needsParens(expr.right) ? `(${rightResult.expr})` : rightResult.expr;
2762
+ const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
2763
+ return { preamble, expr: result };
2734
2764
  }
2735
2765
  case "conditional": {
2736
2766
  const test = this.renderConditionExpr(expr.test);
2737
2767
  return test;
2738
2768
  }
2739
2769
  case "template-literal":
2740
- return this.renderParsedExpr(expr);
2770
+ return plain(this.renderParsedExpr(expr));
2741
2771
  case "arrow-fn":
2742
- return "[ARROW-FN]";
2743
- case "higher-order":
2744
- return this.renderParsedExpr(expr);
2772
+ return plain("[ARROW-FN]");
2773
+ case "higher-order": {
2774
+ const rendered = this.renderParsedExpr(expr);
2775
+ const split = this.splitPreamble(rendered);
2776
+ if (split)
2777
+ return split;
2778
+ return plain(rendered);
2779
+ }
2745
2780
  case "array-literal":
2746
- return this.renderParsedExpr(expr);
2781
+ return plain(this.renderParsedExpr(expr));
2747
2782
  case "array-method":
2748
- return this.renderParsedExpr(expr);
2783
+ return plain(this.renderParsedExpr(expr));
2749
2784
  case "unsupported":
2750
- return expr.raw;
2785
+ return plain(expr.raw);
2751
2786
  }
2752
2787
  }
2753
2788
  renderLoop(loop) {