@barefootjs/go-template 0.17.0 → 0.17.1

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/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  isSupported,
32
32
  exprToString,
33
33
  identifierPath,
34
- asCallbackMethodCall,
34
+ asCallbackMethodCall as asCallbackMethodCall3,
35
35
  sortComparatorFromArrow,
36
36
  emitParsedExpr,
37
37
  emitIRNode,
@@ -40,8 +40,9 @@ import {
40
40
  collectContextConsumers,
41
41
  isLowerableObjectRestDestructure,
42
42
  collectModuleStringConsts as collectModuleStringConstsShared,
43
- searchParamsLocalNames,
44
- prepareLoweringMatchers
43
+ prepareLoweringMatchers,
44
+ envSignalReaderFor,
45
+ computeSsrSeedPlan
45
46
  } from "@barefootjs/jsx";
46
47
  import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
47
48
  import { BF_REGION } from "@barefootjs/shared";
@@ -224,6 +225,13 @@ function emitFlatMapEval(recv, body, param, emit) {
224
225
  const env = emitEvalEnvArg(body, [param], emit);
225
226
  return `bf_flat_map_eval ${wrapIfMultiToken(recv)} "${escapeGoString(json)}" "${param}" ${env}`;
226
227
  }
228
+ function emitMapEval(recv, body, param, emit) {
229
+ const json = serializeParsedExpr(body);
230
+ if (json === null)
231
+ return null;
232
+ const env = emitEvalEnvArg(body, [param], emit);
233
+ return `bf_map_eval ${wrapIfMultiToken(recv)} "${escapeGoString(json)}" "${param}" ${env}`;
234
+ }
227
235
  function stringTolerantEqOperands(l, r) {
228
236
  const isStrLit = (x) => /^"(?:[^"\\]|\\.)*"$/.test(x);
229
237
  if (isStrLit(l) === isStrLit(r))
@@ -337,6 +345,9 @@ class CompileState {
337
345
  currentTypeDefinitions = [];
338
346
  contextConsumers = [];
339
347
  searchParamsLocals = new Set;
348
+ envSignalReadersByLocal = new Map;
349
+ ssrSeedPlan = { baseScope: [], steps: [] };
350
+ hoistedMemoLocals = new Map;
340
351
  loweringMatchers = [];
341
352
  nillablePropNames = new Set;
342
353
  rootScopeNodes = new Set;
@@ -977,8 +988,17 @@ function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackV
977
988
  }
978
989
 
979
990
  // src/adapter/memo/memo-type.ts
991
+ import { asCallbackMethodCall } from "@barefootjs/jsx";
992
+ function isListFilterMemo(memo) {
993
+ if (!memo.parsed)
994
+ return false;
995
+ const cb = asCallbackMethodCall(memo.parsed);
996
+ return cb !== null && cb.method === "filter";
997
+ }
980
998
  function isBooleanMemo(ctx, memo, signals, propsParamMap) {
981
999
  const c = memo.computation;
1000
+ if (isListFilterMemo(memo))
1001
+ return false;
982
1002
  if (isStringTernaryMemo(ctx, memo.parsed))
983
1003
  return false;
984
1004
  if (/(!==|===|!=(?!=)|==(?!=))/.test(c))
@@ -1269,6 +1289,14 @@ function computeObjectMemoInitialValue(ctx, memo) {
1269
1289
  }`;
1270
1290
  }
1271
1291
 
1292
+ // src/adapter/memo/memo-compute.ts
1293
+ import {
1294
+ asCallbackMethodCall as asCallbackMethodCall2,
1295
+ freeVarsInBody as freeVarsInBody2,
1296
+ materializeGetterCalls,
1297
+ serializeParsedExpr as serializeParsedExpr2
1298
+ } from "@barefootjs/jsx";
1299
+
1272
1300
  // src/adapter/memo/template-interp.ts
1273
1301
  import ts from "typescript";
1274
1302
  function computeTemplateLiteralMemoInitialValue(ctx, memo, propsParams) {
@@ -1409,8 +1437,49 @@ function propsAccessNameFromParsed(ctx, node) {
1409
1437
 
1410
1438
  // src/adapter/memo/memo-compute.ts
1411
1439
  var EMPTY_PROP_FALLBACK_VARS2 = new Map;
1440
+ function getterCallName(e) {
1441
+ return e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
1442
+ }
1443
+ function propsMemberName(e) {
1444
+ return e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
1445
+ }
1446
+ function matchFilterArmMemo(ctx, body, signals, propsParams) {
1447
+ const cb = asCallbackMethodCall2(body);
1448
+ if (!cb || cb.method !== "filter")
1449
+ return null;
1450
+ const propName = propsMemberName(cb.object);
1451
+ const param = propName ? propsParams.find((p) => p.name === propName) : undefined;
1452
+ if (!propName || !param)
1453
+ return null;
1454
+ const knownGetterNames = new Set(ctx.state.ssrSeedPlan.steps.filter((s) => s.kind !== "env-reader").map((s) => s.name));
1455
+ const materialized = materializeGetterCalls(cb.arrow.body, knownGetterNames);
1456
+ const predJSON = serializeParsedExpr2(materialized);
1457
+ if (predJSON === null)
1458
+ return null;
1459
+ const paramName = cb.arrow.params[0] ?? "_";
1460
+ const freeVars = freeVarsInBody2(materialized, new Set(cb.arrow.params));
1461
+ return { propName, predJSON, paramName, freeVars };
1462
+ }
1463
+ function filterArmEarlierSiblingRefs(ctx, memo, signals, propsParams) {
1464
+ if (!memo.parsed)
1465
+ return [];
1466
+ const match = matchFilterArmMemo(ctx, memo.parsed, signals, propsParams);
1467
+ if (!match)
1468
+ return [];
1469
+ const memos = ctx.state.currentMemos ?? [];
1470
+ const currentIndex = memos.findIndex((m) => m.name === memo.name);
1471
+ if (currentIndex < 0)
1472
+ return [];
1473
+ const refs = [];
1474
+ for (const name of match.freeVars) {
1475
+ const idx = memos.findIndex((m) => m.name === name);
1476
+ if (idx >= 0 && idx < currentIndex)
1477
+ refs.push(name);
1478
+ }
1479
+ return refs;
1480
+ }
1412
1481
  function computeMemoInitialValue(ctx, memo, signals, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS2, goType) {
1413
- const resolved = computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars);
1482
+ const resolved = computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars, new Set([memo.name]));
1414
1483
  if (resolved !== null)
1415
1484
  return resolved;
1416
1485
  if (goType === "bool")
@@ -1422,15 +1491,79 @@ function computeMemoInitialValue(ctx, memo, signals, propsParams, propFallbackVa
1422
1491
  }
1423
1492
  return "0";
1424
1493
  }
1425
- function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallbackVars) {
1494
+ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallbackVars, currentMemoName, resolving = new Set) {
1426
1495
  const propRef = (propName) => {
1427
1496
  const hoisted = propFallbackVars.get(propName);
1428
1497
  if (hoisted)
1429
1498
  return hoisted.varName;
1430
1499
  return `in.${capitalizeFieldName(propName)}`;
1431
1500
  };
1432
- const getterCallName = (e) => e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
1433
- const propsMemberName = (e) => e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
1501
+ const envGetKey = (e) => {
1502
+ if (e.kind !== "call" || e.callee.kind !== "member" || e.callee.computed)
1503
+ return null;
1504
+ const recvName = getterCallName(e.callee.object);
1505
+ if (recvName === null)
1506
+ return null;
1507
+ const reader = ctx.state.envSignalReadersByLocal.get(recvName);
1508
+ if (!reader || !reader.methods.has(e.callee.property))
1509
+ return null;
1510
+ const arg = e.args[0];
1511
+ if (!arg || arg.kind !== "literal" || arg.literalType !== "string")
1512
+ return null;
1513
+ return { key: String(arg.value), fieldName: capitalizeFieldName(reader.canonicalName) };
1514
+ };
1515
+ {
1516
+ const m = envGetKey(body);
1517
+ if (m !== null)
1518
+ return `in.${m.fieldName}.Get(${JSON.stringify(m.key)})`;
1519
+ }
1520
+ if (body.kind === "logical" && (body.op === "??" || body.op === "||") && body.right.kind === "literal" && body.right.literalType === "string") {
1521
+ const m = envGetKey(body.left);
1522
+ if (m !== null) {
1523
+ const def = JSON.stringify(String(body.right.value));
1524
+ return `func() string { if v := in.${m.fieldName}.Get(${JSON.stringify(m.key)}); v != "" { return v }; return ${def} }()`;
1525
+ }
1526
+ }
1527
+ {
1528
+ const match = matchFilterArmMemo(ctx, body, signals, propsParams);
1529
+ if (match) {
1530
+ const { propName, predJSON, paramName, freeVars } = match;
1531
+ const currentIndex = (ctx.state.currentMemos ?? []).findIndex((m) => m.name === currentMemoName);
1532
+ const envEntries = [];
1533
+ let unresolved = false;
1534
+ for (const name of freeVars) {
1535
+ let goExpr = null;
1536
+ const siblingIndex = (ctx.state.currentMemos ?? []).findIndex((m) => m.name === name);
1537
+ if (siblingIndex >= 0) {
1538
+ const siblingMemo = ctx.state.currentMemos[siblingIndex];
1539
+ const eligible = currentIndex >= 0 && siblingIndex < currentIndex && !resolving.has(siblingMemo.name);
1540
+ if (eligible) {
1541
+ const hoisted = ctx.state.hoistedMemoLocals.get(siblingMemo.name);
1542
+ goExpr = hoisted ?? computeMemoInitialValueOrNull(ctx, siblingMemo, signals, propsParams, propFallbackVars, new Set([...resolving, siblingMemo.name]));
1543
+ }
1544
+ } else {
1545
+ const sig = signals.find((s) => s.getter === name);
1546
+ if (sig) {
1547
+ goExpr = getSignalInitialValueAsGo(ctx, sig.initialValue, propsParams, propFallbackVars);
1548
+ } else {
1549
+ const p = propsParams.find((pp) => pp.name === name);
1550
+ if (p)
1551
+ goExpr = propRef(name);
1552
+ }
1553
+ }
1554
+ if (goExpr === null) {
1555
+ unresolved = true;
1556
+ break;
1557
+ }
1558
+ envEntries.push(`${JSON.stringify(name)}: ${goExpr}`);
1559
+ }
1560
+ if (!unresolved) {
1561
+ const itemsField = `in.${capitalizeFieldName(propName)}`;
1562
+ const envMap = `map[string]any{${envEntries.join(", ")}}`;
1563
+ return `bf.FilterEval(${itemsField}, "${escapeGoString(predJSON)}", ${JSON.stringify(paramName)}, ${envMap})`;
1564
+ }
1565
+ }
1566
+ }
1434
1567
  if (body.kind === "binary" && ["===", "!==", "==", "!="].includes(body.op) && body.right.kind === "literal" && body.right.literalType === "string") {
1435
1568
  const depName = getterCallName(body.left);
1436
1569
  if (depName) {
@@ -1469,8 +1602,8 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1469
1602
  condGo = getSignalInitialValueAsGo(ctx, condSignal.initialValue, propsParams, propFallbackVars);
1470
1603
  } else {
1471
1604
  const condMemo = (ctx.state.currentMemos ?? []).find((m) => m.name === condName);
1472
- if (condMemo) {
1473
- condGo = computeMemoInitialValueOrNull(ctx, condMemo, signals, propsParams, propFallbackVars);
1605
+ if (condMemo && !resolving.has(condMemo.name)) {
1606
+ condGo = computeMemoInitialValueOrNull(ctx, condMemo, signals, propsParams, propFallbackVars, new Set([...resolving, condMemo.name]));
1474
1607
  }
1475
1608
  }
1476
1609
  if (condGo === "true")
@@ -1544,17 +1677,17 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1544
1677
  }
1545
1678
  return null;
1546
1679
  }
1547
- function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS2) {
1680
+ function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS2, resolving = new Set) {
1548
1681
  const computation = memo.computation;
1549
1682
  const tmplMemo = computeTemplateLiteralMemoInitialValue(ctx, memo, propsParams);
1550
1683
  if (tmplMemo !== null)
1551
1684
  return tmplMemo;
1552
1685
  if (memo.parsed) {
1553
- const fromParsed = memoInitialFromParsedBody(ctx, memo.parsed, signals, propsParams, propFallbackVars);
1686
+ const fromParsed = memoInitialFromParsedBody(ctx, memo.parsed, signals, propsParams, propFallbackVars, memo.name, resolving);
1554
1687
  if (fromParsed !== null)
1555
1688
  return fromParsed;
1556
1689
  }
1557
- const cmpTernary = computeComparisonTernaryGo(ctx, memo.parsed, signals, propsParams, propFallbackVars);
1690
+ const cmpTernary = computeComparisonTernaryGo(ctx, memo.parsed, signals, propsParams, propFallbackVars, resolving);
1558
1691
  if (cmpTernary !== null)
1559
1692
  return cmpTernary;
1560
1693
  const blockReturn = resolveBlockBodyMemoModuleConst(ctx, memo, signals);
@@ -1566,20 +1699,22 @@ function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFall
1566
1699
  return objMemo;
1567
1700
  return null;
1568
1701
  }
1569
- function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars) {
1702
+ function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars, resolving = new Set) {
1570
1703
  const signal = signals.find((s) => s.getter === name);
1571
1704
  if (signal) {
1572
1705
  return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
1573
1706
  }
1574
1707
  const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
1575
1708
  if (memo) {
1709
+ if (resolving.has(memo.name))
1710
+ return null;
1576
1711
  const stripped = memo.computation.replace(/^\(\)\s*=>\s*/, "");
1577
1712
  const fb = ctx.extractPropFallback(stripped);
1578
1713
  if (fb && capitalizeFieldName(fb.propName) === capitalizeFieldName(memo.name)) {
1579
1714
  const field = `in.${capitalizeFieldName(fb.propName)}`;
1580
1715
  return `func() interface{} { v := interface{}(${field}); if v == nil || v == "" { return ${fb.goFallback} }; return v }()`;
1581
1716
  }
1582
- return computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars);
1717
+ return computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars, new Set([...resolving, memo.name]));
1583
1718
  }
1584
1719
  const param = propsParams.find((p) => p.name === name);
1585
1720
  if (param) {
@@ -1588,7 +1723,7 @@ function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVar
1588
1723
  }
1589
1724
  return null;
1590
1725
  }
1591
- function computeComparisonTernaryGo(ctx, parsed, signals, propsParams, propFallbackVars) {
1726
+ function computeComparisonTernaryGo(ctx, parsed, signals, propsParams, propFallbackVars, resolving = new Set) {
1592
1727
  if (!parsed || parsed.kind !== "conditional")
1593
1728
  return null;
1594
1729
  const cond = parsed.test;
@@ -1613,16 +1748,16 @@ function computeComparisonTernaryGo(ctx, parsed, signals, propsParams, propFallb
1613
1748
  const f = branch(parsed.alternate);
1614
1749
  if (t === null || f === null)
1615
1750
  return null;
1616
- const condGo = resolveComparisonOperandGo(ctx, cond.left, signals, propsParams, propFallbackVars);
1751
+ const condGo = resolveComparisonOperandGo(ctx, cond.left, signals, propsParams, propFallbackVars, resolving);
1617
1752
  if (condGo === null)
1618
1753
  return null;
1619
1754
  const eqBranch = isEq ? t : f;
1620
1755
  const neBranch = isEq ? f : t;
1621
1756
  return `func() string { if ${condGo} == ${JSON.stringify(cond.right.value)} { return ${eqBranch} }; return ${neBranch} }()`;
1622
1757
  }
1623
- function resolveComparisonOperandGo(ctx, node, signals, propsParams, propFallbackVars) {
1758
+ function resolveComparisonOperandGo(ctx, node, signals, propsParams, propFallbackVars, resolving = new Set) {
1624
1759
  if (node.kind === "call" && node.callee.kind === "identifier" && node.args.length === 0) {
1625
- return resolveGetterValueAsGo(ctx, node.callee.name, signals, propsParams, propFallbackVars);
1760
+ return resolveGetterValueAsGo(ctx, node.callee.name, signals, propsParams, propFallbackVars, resolving);
1626
1761
  }
1627
1762
  if (node.kind === "logical" && node.op === "??" && node.right.kind === "literal" && node.right.literalType === "string") {
1628
1763
  const propName = propsAccessNameFromParsed2(ctx, node.left);
@@ -2001,7 +2136,18 @@ class GoTemplateAdapter extends BaseAdapter {
2001
2136
  this.state.currentMemos = ir.metadata.memos ?? [];
2002
2137
  this.state.currentTypeDefinitions = ir.metadata.typeDefinitions ?? [];
2003
2138
  this.state.contextConsumers = collectContextConsumers(ir.metadata);
2004
- this.state.searchParamsLocals = searchParamsLocalNames(ir.metadata);
2139
+ this.state.ssrSeedPlan = ir.metadata.ssrSeedPlan ?? computeSsrSeedPlan(ir.metadata);
2140
+ this.state.envSignalReadersByLocal = new Map;
2141
+ this.state.searchParamsLocals = new Set;
2142
+ for (const step of this.state.ssrSeedPlan.steps) {
2143
+ if (step.kind !== "env-reader")
2144
+ continue;
2145
+ const reader = envSignalReaderFor(step.reader.key);
2146
+ if (reader)
2147
+ this.state.envSignalReadersByLocal.set(step.name, reader);
2148
+ if (step.reader.key === "search")
2149
+ this.state.searchParamsLocals.add(step.name);
2150
+ }
2005
2151
  this.state.loweringMatchers = prepareLoweringMatchers(ir.metadata);
2006
2152
  augmentInheritedPropAccesses(ir);
2007
2153
  }
@@ -2505,6 +2651,28 @@ ${goFields.join(`
2505
2651
  if (propFallbackVars.size > 0)
2506
2652
  lines.push("");
2507
2653
  this.emitDynamicBodyWrappers(lines, ir, componentName, dynamicWithBody, propFallbackVars, emittedWrapperVars);
2654
+ const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
2655
+ this.state.hoistedMemoLocals = new Map;
2656
+ const hoistNames = new Set;
2657
+ for (const memo of ir.metadata.memos) {
2658
+ for (const name of filterArmEarlierSiblingRefs(this.emitCtx, memo, ir.metadata.signals, ir.metadata.propsParams)) {
2659
+ hoistNames.add(name);
2660
+ }
2661
+ }
2662
+ if (hoistNames.size > 0) {
2663
+ for (const memo of ir.metadata.memos) {
2664
+ if (!hoistNames.has(memo.name))
2665
+ continue;
2666
+ const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
2667
+ const value = computeMemoInitialValue(this.emitCtx, memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
2668
+ let localName = `memo${capitalizeFieldName(memo.name)}`;
2669
+ while (GO_KEYWORDS.has(localName))
2670
+ localName += "_";
2671
+ lines.push(` var ${localName} ${goType} = ${value}`);
2672
+ this.state.hoistedMemoLocals.set(memo.name, localName);
2673
+ }
2674
+ lines.push("");
2675
+ }
2508
2676
  lines.push(` return ${propsTypeName}{`);
2509
2677
  lines.push("\t\tScopeID: scopeID,");
2510
2678
  lines.push("\t\tBfParent: in.BfParent,");
@@ -2578,11 +2746,15 @@ ${goFields.join(`
2578
2746
  continue;
2579
2747
  lines.push(` ${nested.name}s: ${varName},`);
2580
2748
  }
2581
- const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
2582
2749
  for (const memo of ir.metadata.memos) {
2583
2750
  const fieldName = capitalizeFieldName(memo.name);
2584
2751
  if (propFieldNames.has(fieldName))
2585
2752
  continue;
2753
+ const hoistedLocal = this.state.hoistedMemoLocals.get(memo.name);
2754
+ if (hoistedLocal) {
2755
+ lines.push(` ${fieldName}: ${hoistedLocal},`);
2756
+ continue;
2757
+ }
2586
2758
  const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
2587
2759
  const memoValue = computeMemoInitialValue(this.emitCtx, memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
2588
2760
  lines.push(` ${fieldName}: ${memoValue},`);
@@ -3271,12 +3443,14 @@ ${goFields.join(`
3271
3443
  }
3272
3444
  const memo = memos.find((m) => m.name === getterName);
3273
3445
  if (memo) {
3274
- return computeMemoInitialValueOrNull(this.emitCtx, memo, signals, propsParams);
3446
+ return computeMemoInitialValueOrNull(this.emitCtx, memo, signals, propsParams, undefined, new Set([memo.name]));
3275
3447
  }
3276
3448
  }
3277
3449
  return null;
3278
3450
  }
3279
3451
  inferMemoType(memo, signals, propsParamMap) {
3452
+ if (isListFilterMemo(memo))
3453
+ return "[]any";
3280
3454
  if (memo.bodyIsTemplateLiteral)
3281
3455
  return "string";
3282
3456
  if (memo.computation.includes("*") || memo.computation.includes("/") || memo.computation.includes("+") || memo.computation.includes("-")) {
@@ -3798,7 +3972,7 @@ ${goFields.join(`
3798
3972
  "some"
3799
3973
  ]);
3800
3974
  higherOrderShapeOf(node) {
3801
- const cb = asCallbackMethodCall(node);
3975
+ const cb = asCallbackMethodCall3(node);
3802
3976
  if (!cb)
3803
3977
  return null;
3804
3978
  if (!GoTemplateAdapter.PREDICATE_METHODS.has(cb.method))
@@ -3873,6 +4047,12 @@ ${goFields.join(`
3873
4047
  return evalForm;
3874
4048
  return this.pushCallbackBF101(method, true);
3875
4049
  }
4050
+ if (method === "map") {
4051
+ const evalForm = emitMapEval(recv, body, params[0] ?? "_", emit);
4052
+ if (evalForm !== null)
4053
+ return evalForm;
4054
+ return this.pushCallbackBF101(method, true);
4055
+ }
3876
4056
  return this.pushCallbackBF101(method, true);
3877
4057
  }
3878
4058
  arrayMethod(method, object, args, emit) {
@@ -4226,6 +4406,9 @@ ${goFields.join(`
4226
4406
  if (expr.callee.kind === "identifier" && expr.args.length === 0) {
4227
4407
  return `$.${capitalizeFieldName(expr.callee.name)}`;
4228
4408
  }
4409
+ if (asCallbackMethodCall3(expr) !== null) {
4410
+ return this.refuseFilterExprNode(expr);
4411
+ }
4229
4412
  const result = this.renderFilterExpr(expr.callee, param, localVarMap);
4230
4413
  if (this.filterExprUnsupported)
4231
4414
  return "false";
@@ -4290,21 +4473,23 @@ ${goFields.join(`
4290
4473
  }
4291
4474
  return `or (${left}) (${right})`;
4292
4475
  }
4293
- default: {
4294
- this.filterExprUnsupported = true;
4295
- this.state.errors.push({
4296
- code: "BF101",
4297
- severity: "error",
4298
- message: `Filter predicate contains an expression that cannot be lowered to a Go template action: ${exprToString(expr)}`,
4299
- loc: this.makeLoc(),
4300
- suggestion: {
4301
- message: "Options:\n1. Use /* @client */ for client-side evaluation\n2. Rewrite the predicate to avoid nested higher-order methods (`.filter()` / `.map()` / etc. inside the predicate body)"
4302
- }
4303
- });
4304
- return "false";
4305
- }
4476
+ default:
4477
+ return this.refuseFilterExprNode(expr);
4306
4478
  }
4307
4479
  }
4480
+ refuseFilterExprNode(expr) {
4481
+ this.filterExprUnsupported = true;
4482
+ this.state.errors.push({
4483
+ code: "BF101",
4484
+ severity: "error",
4485
+ message: `Filter predicate contains an expression that cannot be lowered to a Go template action: ${exprToString(expr)}`,
4486
+ loc: this.makeLoc(),
4487
+ suggestion: {
4488
+ message: "Options:\n1. Use /* @client */ for client-side evaluation\n2. Rewrite the predicate to avoid nested higher-order methods (`.filter()` / `.map()` / etc. inside the predicate body)"
4489
+ }
4490
+ });
4491
+ return "false";
4492
+ }
4308
4493
  isGoFunctionCall(expr) {
4309
4494
  switch (expr.kind) {
4310
4495
  case "binary":
@@ -4558,7 +4743,7 @@ ${goFields.join(`
4558
4743
  });
4559
4744
  return plain("false");
4560
4745
  }
4561
- if (asCallbackMethodCall(expr)) {
4746
+ if (asCallbackMethodCall3(expr)) {
4562
4747
  const rendered = this.renderParsedExpr(expr);
4563
4748
  const split = this.splitPreamble(rendered);
4564
4749
  if (split)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/go-template",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "Go html/template adapter for BarefootJS - generates Go template files from IR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "directory": "packages/adapter-go-template"
50
50
  },
51
51
  "dependencies": {
52
- "@barefootjs/shared": "0.17.0"
52
+ "@barefootjs/shared": "0.17.1"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@barefootjs/jsx": ">=0.2.0",
@@ -57,6 +57,6 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@barefootjs/adapter-tests": "0.1.0",
60
- "@barefootjs/jsx": "0.17.0"
60
+ "@barefootjs/jsx": "0.17.1"
61
61
  }
62
62
  }