@barefootjs/go-template 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/build.js CHANGED
@@ -708,6 +708,8 @@ class CompileState {
708
708
  nillablePropNames = new Set;
709
709
  nullishConsumedPropNames = new Set;
710
710
  omittableAttrConsumedPropNames = new Set;
711
+ textConsumedPropNames = new Set;
712
+ presenceCheckedPropNames = new Set;
711
713
  stringValueNames = new Set;
712
714
  rootScopeNodes = new Set;
713
715
  memoBackedLoopSlice = new Map;
@@ -1451,15 +1453,28 @@ function parsedLiteralToGo(ctx, expr, typeInfo) {
1451
1453
 
1452
1454
  // src/adapter/value/value-lowering.ts
1453
1455
  var EMPTY_PROP_FALLBACK_VARS = new Map;
1456
+ function nillableAwarePropRef(ctx, propName, expectedType) {
1457
+ const fieldRef = `in.${capitalizeFieldName(propName)}`;
1458
+ const scalar = expectedType.kind === "primitive" ? expectedType : expectedType.kind === "union" && expectedType.unionTypes?.length === 2 ? expectedType.unionTypes.find((t) => t.primitive !== "undefined" && t.primitive !== "null") : undefined;
1459
+ if (ctx.state.nillablePropNames.has(propName) && scalar?.kind === "primitive") {
1460
+ const goType = scalar.primitive === "boolean" ? "bool" : scalar.primitive === "number" ? "float64" : scalar.primitive === "string" ? "string" : null;
1461
+ if (goType) {
1462
+ const zero = goType === "bool" ? "false" : goType === "string" ? '""' : "0";
1463
+ return `func() ${goType} { if v, ok := ${fieldRef}.(${goType}); ok { return v }; return ${zero} }()`;
1464
+ }
1465
+ }
1466
+ return fieldRef;
1467
+ }
1454
1468
  function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
1469
+ const propRef = (propName2) => nillableAwarePropRef(ctx, propName2, typeInfo);
1455
1470
  if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
1456
1471
  if (propsParams?.some((p) => p.name === value)) {
1457
- return `in.${capitalizeFieldName(value)}`;
1472
+ return propRef(value);
1458
1473
  }
1459
1474
  }
1460
1475
  const propName = ctx.extractPropNameFromInitialValue(value, preParsed);
1461
1476
  if (propName && propsParams?.some((p) => p.name === propName)) {
1462
- return `in.${capitalizeFieldName(propName)}`;
1477
+ return propRef(propName);
1463
1478
  }
1464
1479
  if (typeInfo.kind === "primitive") {
1465
1480
  if (typeInfo.primitive === "boolean") {
@@ -1526,19 +1541,20 @@ function objectLiteralToGoMap(ctx, expr) {
1526
1541
  return null;
1527
1542
  return `map[string]interface{}{${entries.join(", ")}}`;
1528
1543
  }
1529
- function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS) {
1544
+ function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS, signalType) {
1545
+ const propRef = (propName2) => signalType ? nillableAwarePropRef(ctx, propName2, signalType) : `in.${capitalizeFieldName(propName2)}`;
1530
1546
  if (propsParams.some((p) => p.name === initialValue)) {
1531
1547
  const hoisted = propFallbackVars.get(initialValue);
1532
1548
  if (hoisted)
1533
1549
  return hoisted.varName;
1534
- return `in.${capitalizeFieldName(initialValue)}`;
1550
+ return propRef(initialValue);
1535
1551
  }
1536
1552
  const propName = ctx.extractPropNameFromInitialValue(initialValue);
1537
1553
  if (propName && propsParams.some((p) => p.name === propName)) {
1538
1554
  const hoisted = propFallbackVars.get(propName);
1539
1555
  if (hoisted)
1540
1556
  return hoisted.varName;
1541
- return `in.${capitalizeFieldName(propName)}`;
1557
+ return propRef(propName);
1542
1558
  }
1543
1559
  if (/^-?\d+$/.test(initialValue)) {
1544
1560
  return initialValue;
@@ -2008,9 +2024,34 @@ var EMPTY_PROP_FALLBACK_VARS2 = new Map;
2008
2024
  function getterCallName(e) {
2009
2025
  return e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
2010
2026
  }
2027
+ function isBooleanTypeInfo(t) {
2028
+ if (t.kind === "primitive")
2029
+ return t.primitive === "boolean";
2030
+ if (t.kind === "union" && t.unionTypes?.length === 2) {
2031
+ const scalar = t.unionTypes.find((u) => u.primitive !== "undefined" && u.primitive !== "null");
2032
+ return scalar?.kind === "primitive" && scalar.primitive === "boolean";
2033
+ }
2034
+ return false;
2035
+ }
2036
+ function isBooleanTypedGetter(ctx, name, signals) {
2037
+ const signal = signals.find((s) => s.getter === name);
2038
+ if (signal)
2039
+ return signal.type !== undefined && isBooleanTypeInfo(signal.type);
2040
+ const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
2041
+ return memo?.type !== undefined && isBooleanTypeInfo(memo.type);
2042
+ }
2011
2043
  function propsMemberName(e) {
2012
2044
  return e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
2013
2045
  }
2046
+ function propNameForPropsBinding(ctx, e) {
2047
+ const propsObject = ctx.state.propsObjectName;
2048
+ if (e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === propsObject) {
2049
+ return e.property;
2050
+ }
2051
+ if (!propsObject && e.kind === "identifier")
2052
+ return e.name;
2053
+ return null;
2054
+ }
2014
2055
  function matchFilterArmMemo(ctx, body, signals, propsParams) {
2015
2056
  const cb = asCallbackMethodCall2(body);
2016
2057
  if (!cb || cb.method !== "filter")
@@ -2149,6 +2190,17 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
2149
2190
  if (propName)
2150
2191
  return propRef(propName);
2151
2192
  }
2193
+ if (body.kind === "binary" && (body.op === "!==" || body.op === "!=" || body.op === "===" || body.op === "==")) {
2194
+ const other = body.right.kind === "identifier" && body.right.name === "undefined" ? body.left : body.left.kind === "identifier" && body.left.name === "undefined" ? body.right : null;
2195
+ const propName = other ? propNameForPropsBinding(ctx, other) : null;
2196
+ if (propName) {
2197
+ const param = propsParams.find((p) => p.name === propName);
2198
+ if (param && ctx.state.nillablePropNames.has(propName)) {
2199
+ const isNe = body.op === "!==" || body.op === "!=";
2200
+ return `in.${capitalizeFieldName(propName)} ${isNe ? "!=" : "=="} nil`;
2201
+ }
2202
+ }
2203
+ }
2152
2204
  if (body.kind === "conditional") {
2153
2205
  const condName = getterCallName(body.test);
2154
2206
  if (condName) {
@@ -2183,6 +2235,18 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
2183
2235
  }
2184
2236
  }
2185
2237
  }
2238
+ if (condName) {
2239
+ const tName = getterCallName(body.consequent);
2240
+ const fName = getterCallName(body.alternate);
2241
+ if (tName && fName && isBooleanTypedGetter(ctx, tName, signals) && isBooleanTypedGetter(ctx, fName, signals)) {
2242
+ const condGo = resolveGetterValueAsGo(ctx, condName, signals, propsParams, propFallbackVars, resolving);
2243
+ const tGo = resolveGetterValueAsGo(ctx, tName, signals, propsParams, propFallbackVars, resolving);
2244
+ const fGo = resolveGetterValueAsGo(ctx, fName, signals, propsParams, propFallbackVars, resolving);
2245
+ if (condGo !== null && tGo !== null && fGo !== null) {
2246
+ return `func() bool { if ${condGo} { return ${tGo} }; return ${fGo} }()`;
2247
+ }
2248
+ }
2249
+ }
2186
2250
  }
2187
2251
  if (body.kind === "binary" && ["*", "+", "-", "/"].includes(body.op) && body.right.kind === "literal" && body.right.literalType === "number" && typeof body.right.value === "number" && Number.isInteger(body.right.value) && body.right.value >= 0) {
2188
2252
  const operator = body.op;
@@ -2271,7 +2335,7 @@ function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFall
2271
2335
  function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars, resolving = new Set) {
2272
2336
  const signal = signals.find((s) => s.getter === name);
2273
2337
  if (signal) {
2274
- return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
2338
+ return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars, signal.type);
2275
2339
  }
2276
2340
  const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
2277
2341
  if (memo) {
@@ -2673,8 +2737,10 @@ function collectNullishConsumedPropNames(ctx, ir) {
2673
2737
  const propNameOfLeft = (left) => {
2674
2738
  if (left.kind === "identifier")
2675
2739
  return left.name;
2676
- if (left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === propsObject) {
2677
- return left.property;
2740
+ if (left.kind === "member" && !left.computed && left.object.kind === "identifier") {
2741
+ if (left.object.name === propsObject)
2742
+ return left.property;
2743
+ return left.object.name;
2678
2744
  }
2679
2745
  return null;
2680
2746
  };
@@ -2745,12 +2811,60 @@ function collectOmittableAttrConsumedPropNames(ctx, ir) {
2745
2811
  walk(ir.root);
2746
2812
  return names;
2747
2813
  }
2814
+ function collectTextConsumedPropNames(ctx, ir) {
2815
+ const names = new Set;
2816
+ const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null && p.type.kind === "primitive").map((p) => p.name));
2817
+ if (optionalParams.size === 0)
2818
+ return names;
2819
+ const propsObject = ctx.state.propsObjectName;
2820
+ const walk = (node) => {
2821
+ if (!node || typeof node !== "object")
2822
+ return;
2823
+ if (Array.isArray(node)) {
2824
+ for (const item of node)
2825
+ walk(item);
2826
+ return;
2827
+ }
2828
+ const rec = node;
2829
+ if (rec.type === "expression") {
2830
+ const bareId = String(rec.expr ?? "").trim();
2831
+ const propName = propsObject && bareId.startsWith(`${propsObject}.`) ? bareId.slice(propsObject.length + 1) : bareId;
2832
+ if (/^[A-Za-z_$][\w$]*$/.test(propName) && optionalParams.has(propName)) {
2833
+ names.add(propName);
2834
+ }
2835
+ }
2836
+ for (const value of Object.values(rec))
2837
+ walk(value);
2838
+ };
2839
+ walk(ir.root);
2840
+ return names;
2841
+ }
2842
+ function collectPresenceCheckedPropNames(ctx, ir) {
2843
+ const names = new Set;
2844
+ const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null && p.type.kind === "primitive").map((p) => p.name));
2845
+ if (optionalParams.size === 0)
2846
+ return names;
2847
+ const propsObject = ctx.state.propsObjectName;
2848
+ const isUndefinedCheck = (op) => op === "!==" || op === "!=" || op === "===" || op === "==";
2849
+ for (const memo of ir.metadata.memos) {
2850
+ const body = memo.parsed;
2851
+ if (!body || body.kind !== "binary" || !isUndefinedCheck(body.op))
2852
+ continue;
2853
+ const other = body.right.kind === "identifier" && body.right.name === "undefined" ? body.left : body.left.kind === "identifier" && body.left.name === "undefined" ? body.right : null;
2854
+ if (!other)
2855
+ continue;
2856
+ const propName = other.kind === "member" && !other.computed && other.object.kind === "identifier" && other.object.name === propsObject ? other.property : !propsObject && other.kind === "identifier" ? other.name : null;
2857
+ if (propName && optionalParams.has(propName))
2858
+ names.add(propName);
2859
+ }
2860
+ return names;
2861
+ }
2748
2862
  function resolvePropGoType(ctx, param, propTypeOverrides) {
2749
2863
  const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
2750
2864
  if (param.optional && ctx.state.localStructFields.has(base)) {
2751
2865
  return "map[string]interface{}";
2752
2866
  }
2753
- if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
2867
+ if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name) || ctx.state.textConsumedPropNames.has(param.name) || ctx.state.presenceCheckedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
2754
2868
  return "interface{}";
2755
2869
  }
2756
2870
  return base;
@@ -2887,6 +3001,8 @@ class GoTemplateAdapter extends BaseAdapter {
2887
3001
  this.buildLocalTypeTables(ir, ir.metadata.componentName);
2888
3002
  this.state.nullishConsumedPropNames = collectNullishConsumedPropNames(this.emitCtx, ir);
2889
3003
  this.state.omittableAttrConsumedPropNames = collectOmittableAttrConsumedPropNames(this.emitCtx, ir);
3004
+ this.state.textConsumedPropNames = collectTextConsumedPropNames(this.emitCtx, ir);
3005
+ this.state.presenceCheckedPropNames = collectPresenceCheckedPropNames(this.emitCtx, ir);
2890
3006
  this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
2891
3007
  }
2892
3008
  generate(ir, options) {
@@ -4569,10 +4685,22 @@ ${goFields.join(`
4569
4685
  }
4570
4686
  return goExpr;
4571
4687
  }
4688
+ const finalExpr = this.textNillablePropNameOf(classify.parsed) !== null ? `bf_string ${wrapIfMultiToken(goExpr)}` : goExpr;
4572
4689
  if (expr.slotId) {
4573
- return `{{bfTextStart "${expr.slotId}"}}{{${goExpr}}}{{bfTextEnd}}`;
4690
+ return `{{bfTextStart "${expr.slotId}"}}{{${finalExpr}}}{{bfTextEnd}}`;
4574
4691
  }
4575
- return `{{${goExpr}}}`;
4692
+ return `{{${finalExpr}}}`;
4693
+ }
4694
+ textNillablePropNameOf(expr) {
4695
+ if (!expr)
4696
+ return null;
4697
+ let name = null;
4698
+ if (expr.kind === "identifier") {
4699
+ name = expr.name;
4700
+ } else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
4701
+ name = expr.property;
4702
+ }
4703
+ return name !== null && this.state.textConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
4576
4704
  }
4577
4705
  isTemplateFragment(go, kind) {
4578
4706
  return go.startsWith("{{") || kind === "template-literal";
@@ -4788,7 +4916,7 @@ ${goFields.join(`
4788
4916
  }
4789
4917
  const obj = emit(object);
4790
4918
  if (property === "length")
4791
- return `len ${obj}`;
4919
+ return `bf_length ${wrapIfMultiToken(obj)}`;
4792
4920
  if (optional) {
4793
4921
  return `bf_get ${wrapIfMultiToken(obj)} ${JSON.stringify(goFieldNameForKey(property))}`;
4794
4922
  }
@@ -4866,8 +4994,12 @@ ${goFields.join(`
4866
4994
  let name = null;
4867
4995
  if (expr.kind === "identifier") {
4868
4996
  name = expr.name;
4869
- } else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
4870
- name = expr.property;
4997
+ } else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier") {
4998
+ if (expr.object.name === this.state.propsObjectName) {
4999
+ name = expr.property;
5000
+ } else {
5001
+ name = expr.object.name;
5002
+ }
4871
5003
  }
4872
5004
  return name !== null && this.state.nullishConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
4873
5005
  }
@@ -5302,9 +5434,6 @@ ${goFields.join(`
5302
5434
  renderPredicateCondition(pred, param, datumField) {
5303
5435
  return this.renderFilterExpr(pred, param, new Map, datumField ?? undefined);
5304
5436
  }
5305
- needsParens(expr) {
5306
- return expr.kind === "logical" || expr.kind === "unary" || expr.kind === "conditional";
5307
- }
5308
5437
  splitPreamble(rendered) {
5309
5438
  if (!rendered.includes("{{"))
5310
5439
  return null;
@@ -5717,7 +5846,8 @@ ${goFields.join(`
5717
5846
  return plain(this.rootFieldRef(expr.callee.name));
5718
5847
  }
5719
5848
  if (expr.callee.kind === "identifier" && (identifierPath(expr.callee) ?? expr.callee.name) === "isValidElement" && expr.args.length === 1) {
5720
- return this.renderConditionExpr(expr.args[0]);
5849
+ const inner = this.renderConditionExpr(expr.args[0]);
5850
+ return { preamble: inner.preamble, expr: `(bf_is_element ${wrapIfMultiToken(inner.expr)})` };
5721
5851
  }
5722
5852
  if (expr.callee.kind === "identifier" && !this.templatePrimitives[identifierPath(expr.callee) ?? ""]) {
5723
5853
  const path = identifierPath(expr.callee) ?? expr.callee.name;
@@ -5834,9 +5964,9 @@ ${goFields.join(`
5834
5964
  const leftResult = this.renderConditionExpr(expr.left);
5835
5965
  const rightResult = this.renderConditionExpr(expr.right);
5836
5966
  const preamble = leftResult.preamble + rightResult.preamble;
5837
- const wrapLeft = this.needsParens(expr.left) ? `(${leftResult.expr})` : leftResult.expr;
5838
- const wrapRight = this.needsParens(expr.right) ? `(${rightResult.expr})` : rightResult.expr;
5839
- const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
5967
+ const wrapLeft = wrapIfMultiToken(leftResult.expr);
5968
+ const wrapRight = wrapIfMultiToken(rightResult.expr);
5969
+ const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : expr.op === "??" && this.nillablePropNameOf(expr.left) !== null ? `bf_nullish ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
5840
5970
  return { preamble, expr: result };
5841
5971
  }
5842
5972
  case "conditional": {
@@ -6249,7 +6379,11 @@ ${children}`;
6249
6379
  if (e.kind === "expr" && !isSupported(parseExpression4(e.expr)).supported)
6250
6380
  return null;
6251
6381
  }
6252
- return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:{{${this.convertExpressionToGo(e.expr)}}}`).join(";");
6382
+ const args = entries.flatMap((e) => [
6383
+ JSON.stringify(e.cssKey),
6384
+ e.kind === "literal" ? JSON.stringify(e.value) : wrapIfMultiToken(this.convertExpressionToGo(e.expr))
6385
+ ]);
6386
+ return `{{bf_style_object ${args.join(" ")}}}`;
6253
6387
  }
6254
6388
  renderAttributes(element) {
6255
6389
  const parts = [];
@@ -1 +1 @@
1
- {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA+H7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAqI7B,CAAA"}
package/dist/index.js CHANGED
@@ -368,6 +368,8 @@ class CompileState {
368
368
  nillablePropNames = new Set;
369
369
  nullishConsumedPropNames = new Set;
370
370
  omittableAttrConsumedPropNames = new Set;
371
+ textConsumedPropNames = new Set;
372
+ presenceCheckedPropNames = new Set;
371
373
  stringValueNames = new Set;
372
374
  rootScopeNodes = new Set;
373
375
  memoBackedLoopSlice = new Map;
@@ -1111,15 +1113,28 @@ function parsedLiteralToGo(ctx, expr, typeInfo) {
1111
1113
 
1112
1114
  // src/adapter/value/value-lowering.ts
1113
1115
  var EMPTY_PROP_FALLBACK_VARS = new Map;
1116
+ function nillableAwarePropRef(ctx, propName, expectedType) {
1117
+ const fieldRef = `in.${capitalizeFieldName(propName)}`;
1118
+ const scalar = expectedType.kind === "primitive" ? expectedType : expectedType.kind === "union" && expectedType.unionTypes?.length === 2 ? expectedType.unionTypes.find((t) => t.primitive !== "undefined" && t.primitive !== "null") : undefined;
1119
+ if (ctx.state.nillablePropNames.has(propName) && scalar?.kind === "primitive") {
1120
+ const goType = scalar.primitive === "boolean" ? "bool" : scalar.primitive === "number" ? "float64" : scalar.primitive === "string" ? "string" : null;
1121
+ if (goType) {
1122
+ const zero = goType === "bool" ? "false" : goType === "string" ? '""' : "0";
1123
+ return `func() ${goType} { if v, ok := ${fieldRef}.(${goType}); ok { return v }; return ${zero} }()`;
1124
+ }
1125
+ }
1126
+ return fieldRef;
1127
+ }
1114
1128
  function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
1129
+ const propRef = (propName2) => nillableAwarePropRef(ctx, propName2, typeInfo);
1115
1130
  if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
1116
1131
  if (propsParams?.some((p) => p.name === value)) {
1117
- return `in.${capitalizeFieldName(value)}`;
1132
+ return propRef(value);
1118
1133
  }
1119
1134
  }
1120
1135
  const propName = ctx.extractPropNameFromInitialValue(value, preParsed);
1121
1136
  if (propName && propsParams?.some((p) => p.name === propName)) {
1122
- return `in.${capitalizeFieldName(propName)}`;
1137
+ return propRef(propName);
1123
1138
  }
1124
1139
  if (typeInfo.kind === "primitive") {
1125
1140
  if (typeInfo.primitive === "boolean") {
@@ -1186,19 +1201,20 @@ function objectLiteralToGoMap(ctx, expr) {
1186
1201
  return null;
1187
1202
  return `map[string]interface{}{${entries.join(", ")}}`;
1188
1203
  }
1189
- function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS) {
1204
+ function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS, signalType) {
1205
+ const propRef = (propName2) => signalType ? nillableAwarePropRef(ctx, propName2, signalType) : `in.${capitalizeFieldName(propName2)}`;
1190
1206
  if (propsParams.some((p) => p.name === initialValue)) {
1191
1207
  const hoisted = propFallbackVars.get(initialValue);
1192
1208
  if (hoisted)
1193
1209
  return hoisted.varName;
1194
- return `in.${capitalizeFieldName(initialValue)}`;
1210
+ return propRef(initialValue);
1195
1211
  }
1196
1212
  const propName = ctx.extractPropNameFromInitialValue(initialValue);
1197
1213
  if (propName && propsParams.some((p) => p.name === propName)) {
1198
1214
  const hoisted = propFallbackVars.get(propName);
1199
1215
  if (hoisted)
1200
1216
  return hoisted.varName;
1201
- return `in.${capitalizeFieldName(propName)}`;
1217
+ return propRef(propName);
1202
1218
  }
1203
1219
  if (/^-?\d+$/.test(initialValue)) {
1204
1220
  return initialValue;
@@ -1668,9 +1684,34 @@ var EMPTY_PROP_FALLBACK_VARS2 = new Map;
1668
1684
  function getterCallName(e) {
1669
1685
  return e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
1670
1686
  }
1687
+ function isBooleanTypeInfo(t) {
1688
+ if (t.kind === "primitive")
1689
+ return t.primitive === "boolean";
1690
+ if (t.kind === "union" && t.unionTypes?.length === 2) {
1691
+ const scalar = t.unionTypes.find((u) => u.primitive !== "undefined" && u.primitive !== "null");
1692
+ return scalar?.kind === "primitive" && scalar.primitive === "boolean";
1693
+ }
1694
+ return false;
1695
+ }
1696
+ function isBooleanTypedGetter(ctx, name, signals) {
1697
+ const signal = signals.find((s) => s.getter === name);
1698
+ if (signal)
1699
+ return signal.type !== undefined && isBooleanTypeInfo(signal.type);
1700
+ const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
1701
+ return memo?.type !== undefined && isBooleanTypeInfo(memo.type);
1702
+ }
1671
1703
  function propsMemberName(e) {
1672
1704
  return e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
1673
1705
  }
1706
+ function propNameForPropsBinding(ctx, e) {
1707
+ const propsObject = ctx.state.propsObjectName;
1708
+ if (e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === propsObject) {
1709
+ return e.property;
1710
+ }
1711
+ if (!propsObject && e.kind === "identifier")
1712
+ return e.name;
1713
+ return null;
1714
+ }
1674
1715
  function matchFilterArmMemo(ctx, body, signals, propsParams) {
1675
1716
  const cb = asCallbackMethodCall2(body);
1676
1717
  if (!cb || cb.method !== "filter")
@@ -1809,6 +1850,17 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1809
1850
  if (propName)
1810
1851
  return propRef(propName);
1811
1852
  }
1853
+ if (body.kind === "binary" && (body.op === "!==" || body.op === "!=" || body.op === "===" || body.op === "==")) {
1854
+ const other = body.right.kind === "identifier" && body.right.name === "undefined" ? body.left : body.left.kind === "identifier" && body.left.name === "undefined" ? body.right : null;
1855
+ const propName = other ? propNameForPropsBinding(ctx, other) : null;
1856
+ if (propName) {
1857
+ const param = propsParams.find((p) => p.name === propName);
1858
+ if (param && ctx.state.nillablePropNames.has(propName)) {
1859
+ const isNe = body.op === "!==" || body.op === "!=";
1860
+ return `in.${capitalizeFieldName(propName)} ${isNe ? "!=" : "=="} nil`;
1861
+ }
1862
+ }
1863
+ }
1812
1864
  if (body.kind === "conditional") {
1813
1865
  const condName = getterCallName(body.test);
1814
1866
  if (condName) {
@@ -1843,6 +1895,18 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1843
1895
  }
1844
1896
  }
1845
1897
  }
1898
+ if (condName) {
1899
+ const tName = getterCallName(body.consequent);
1900
+ const fName = getterCallName(body.alternate);
1901
+ if (tName && fName && isBooleanTypedGetter(ctx, tName, signals) && isBooleanTypedGetter(ctx, fName, signals)) {
1902
+ const condGo = resolveGetterValueAsGo(ctx, condName, signals, propsParams, propFallbackVars, resolving);
1903
+ const tGo = resolveGetterValueAsGo(ctx, tName, signals, propsParams, propFallbackVars, resolving);
1904
+ const fGo = resolveGetterValueAsGo(ctx, fName, signals, propsParams, propFallbackVars, resolving);
1905
+ if (condGo !== null && tGo !== null && fGo !== null) {
1906
+ return `func() bool { if ${condGo} { return ${tGo} }; return ${fGo} }()`;
1907
+ }
1908
+ }
1909
+ }
1846
1910
  }
1847
1911
  if (body.kind === "binary" && ["*", "+", "-", "/"].includes(body.op) && body.right.kind === "literal" && body.right.literalType === "number" && typeof body.right.value === "number" && Number.isInteger(body.right.value) && body.right.value >= 0) {
1848
1912
  const operator = body.op;
@@ -1931,7 +1995,7 @@ function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFall
1931
1995
  function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars, resolving = new Set) {
1932
1996
  const signal = signals.find((s) => s.getter === name);
1933
1997
  if (signal) {
1934
- return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
1998
+ return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars, signal.type);
1935
1999
  }
1936
2000
  const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
1937
2001
  if (memo) {
@@ -2333,8 +2397,10 @@ function collectNullishConsumedPropNames(ctx, ir) {
2333
2397
  const propNameOfLeft = (left) => {
2334
2398
  if (left.kind === "identifier")
2335
2399
  return left.name;
2336
- if (left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === propsObject) {
2337
- return left.property;
2400
+ if (left.kind === "member" && !left.computed && left.object.kind === "identifier") {
2401
+ if (left.object.name === propsObject)
2402
+ return left.property;
2403
+ return left.object.name;
2338
2404
  }
2339
2405
  return null;
2340
2406
  };
@@ -2405,12 +2471,60 @@ function collectOmittableAttrConsumedPropNames(ctx, ir) {
2405
2471
  walk(ir.root);
2406
2472
  return names;
2407
2473
  }
2474
+ function collectTextConsumedPropNames(ctx, ir) {
2475
+ const names = new Set;
2476
+ const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null && p.type.kind === "primitive").map((p) => p.name));
2477
+ if (optionalParams.size === 0)
2478
+ return names;
2479
+ const propsObject = ctx.state.propsObjectName;
2480
+ const walk = (node) => {
2481
+ if (!node || typeof node !== "object")
2482
+ return;
2483
+ if (Array.isArray(node)) {
2484
+ for (const item of node)
2485
+ walk(item);
2486
+ return;
2487
+ }
2488
+ const rec = node;
2489
+ if (rec.type === "expression") {
2490
+ const bareId = String(rec.expr ?? "").trim();
2491
+ const propName = propsObject && bareId.startsWith(`${propsObject}.`) ? bareId.slice(propsObject.length + 1) : bareId;
2492
+ if (/^[A-Za-z_$][\w$]*$/.test(propName) && optionalParams.has(propName)) {
2493
+ names.add(propName);
2494
+ }
2495
+ }
2496
+ for (const value of Object.values(rec))
2497
+ walk(value);
2498
+ };
2499
+ walk(ir.root);
2500
+ return names;
2501
+ }
2502
+ function collectPresenceCheckedPropNames(ctx, ir) {
2503
+ const names = new Set;
2504
+ const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null && p.type.kind === "primitive").map((p) => p.name));
2505
+ if (optionalParams.size === 0)
2506
+ return names;
2507
+ const propsObject = ctx.state.propsObjectName;
2508
+ const isUndefinedCheck = (op) => op === "!==" || op === "!=" || op === "===" || op === "==";
2509
+ for (const memo of ir.metadata.memos) {
2510
+ const body = memo.parsed;
2511
+ if (!body || body.kind !== "binary" || !isUndefinedCheck(body.op))
2512
+ continue;
2513
+ const other = body.right.kind === "identifier" && body.right.name === "undefined" ? body.left : body.left.kind === "identifier" && body.left.name === "undefined" ? body.right : null;
2514
+ if (!other)
2515
+ continue;
2516
+ const propName = other.kind === "member" && !other.computed && other.object.kind === "identifier" && other.object.name === propsObject ? other.property : !propsObject && other.kind === "identifier" ? other.name : null;
2517
+ if (propName && optionalParams.has(propName))
2518
+ names.add(propName);
2519
+ }
2520
+ return names;
2521
+ }
2408
2522
  function resolvePropGoType(ctx, param, propTypeOverrides) {
2409
2523
  const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
2410
2524
  if (param.optional && ctx.state.localStructFields.has(base)) {
2411
2525
  return "map[string]interface{}";
2412
2526
  }
2413
- if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
2527
+ if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name) || ctx.state.textConsumedPropNames.has(param.name) || ctx.state.presenceCheckedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
2414
2528
  return "interface{}";
2415
2529
  }
2416
2530
  return base;
@@ -2547,6 +2661,8 @@ class GoTemplateAdapter extends BaseAdapter {
2547
2661
  this.buildLocalTypeTables(ir, ir.metadata.componentName);
2548
2662
  this.state.nullishConsumedPropNames = collectNullishConsumedPropNames(this.emitCtx, ir);
2549
2663
  this.state.omittableAttrConsumedPropNames = collectOmittableAttrConsumedPropNames(this.emitCtx, ir);
2664
+ this.state.textConsumedPropNames = collectTextConsumedPropNames(this.emitCtx, ir);
2665
+ this.state.presenceCheckedPropNames = collectPresenceCheckedPropNames(this.emitCtx, ir);
2550
2666
  this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
2551
2667
  }
2552
2668
  generate(ir, options) {
@@ -4229,10 +4345,22 @@ ${goFields.join(`
4229
4345
  }
4230
4346
  return goExpr;
4231
4347
  }
4348
+ const finalExpr = this.textNillablePropNameOf(classify.parsed) !== null ? `bf_string ${wrapIfMultiToken(goExpr)}` : goExpr;
4232
4349
  if (expr.slotId) {
4233
- return `{{bfTextStart "${expr.slotId}"}}{{${goExpr}}}{{bfTextEnd}}`;
4350
+ return `{{bfTextStart "${expr.slotId}"}}{{${finalExpr}}}{{bfTextEnd}}`;
4234
4351
  }
4235
- return `{{${goExpr}}}`;
4352
+ return `{{${finalExpr}}}`;
4353
+ }
4354
+ textNillablePropNameOf(expr) {
4355
+ if (!expr)
4356
+ return null;
4357
+ let name = null;
4358
+ if (expr.kind === "identifier") {
4359
+ name = expr.name;
4360
+ } else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
4361
+ name = expr.property;
4362
+ }
4363
+ return name !== null && this.state.textConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
4236
4364
  }
4237
4365
  isTemplateFragment(go, kind) {
4238
4366
  return go.startsWith("{{") || kind === "template-literal";
@@ -4448,7 +4576,7 @@ ${goFields.join(`
4448
4576
  }
4449
4577
  const obj = emit(object);
4450
4578
  if (property === "length")
4451
- return `len ${obj}`;
4579
+ return `bf_length ${wrapIfMultiToken(obj)}`;
4452
4580
  if (optional) {
4453
4581
  return `bf_get ${wrapIfMultiToken(obj)} ${JSON.stringify(goFieldNameForKey(property))}`;
4454
4582
  }
@@ -4526,8 +4654,12 @@ ${goFields.join(`
4526
4654
  let name = null;
4527
4655
  if (expr.kind === "identifier") {
4528
4656
  name = expr.name;
4529
- } else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
4530
- name = expr.property;
4657
+ } else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier") {
4658
+ if (expr.object.name === this.state.propsObjectName) {
4659
+ name = expr.property;
4660
+ } else {
4661
+ name = expr.object.name;
4662
+ }
4531
4663
  }
4532
4664
  return name !== null && this.state.nullishConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
4533
4665
  }
@@ -4962,9 +5094,6 @@ ${goFields.join(`
4962
5094
  renderPredicateCondition(pred, param, datumField) {
4963
5095
  return this.renderFilterExpr(pred, param, new Map, datumField ?? undefined);
4964
5096
  }
4965
- needsParens(expr) {
4966
- return expr.kind === "logical" || expr.kind === "unary" || expr.kind === "conditional";
4967
- }
4968
5097
  splitPreamble(rendered) {
4969
5098
  if (!rendered.includes("{{"))
4970
5099
  return null;
@@ -5377,7 +5506,8 @@ ${goFields.join(`
5377
5506
  return plain(this.rootFieldRef(expr.callee.name));
5378
5507
  }
5379
5508
  if (expr.callee.kind === "identifier" && (identifierPath(expr.callee) ?? expr.callee.name) === "isValidElement" && expr.args.length === 1) {
5380
- return this.renderConditionExpr(expr.args[0]);
5509
+ const inner = this.renderConditionExpr(expr.args[0]);
5510
+ return { preamble: inner.preamble, expr: `(bf_is_element ${wrapIfMultiToken(inner.expr)})` };
5381
5511
  }
5382
5512
  if (expr.callee.kind === "identifier" && !this.templatePrimitives[identifierPath(expr.callee) ?? ""]) {
5383
5513
  const path = identifierPath(expr.callee) ?? expr.callee.name;
@@ -5494,9 +5624,9 @@ ${goFields.join(`
5494
5624
  const leftResult = this.renderConditionExpr(expr.left);
5495
5625
  const rightResult = this.renderConditionExpr(expr.right);
5496
5626
  const preamble = leftResult.preamble + rightResult.preamble;
5497
- const wrapLeft = this.needsParens(expr.left) ? `(${leftResult.expr})` : leftResult.expr;
5498
- const wrapRight = this.needsParens(expr.right) ? `(${rightResult.expr})` : rightResult.expr;
5499
- const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
5627
+ const wrapLeft = wrapIfMultiToken(leftResult.expr);
5628
+ const wrapRight = wrapIfMultiToken(rightResult.expr);
5629
+ const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : expr.op === "??" && this.nillablePropNameOf(expr.left) !== null ? `bf_nullish ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
5500
5630
  return { preamble, expr: result };
5501
5631
  }
5502
5632
  case "conditional": {
@@ -5909,7 +6039,11 @@ ${children}`;
5909
6039
  if (e.kind === "expr" && !isSupported(parseExpression4(e.expr)).supported)
5910
6040
  return null;
5911
6041
  }
5912
- return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:{{${this.convertExpressionToGo(e.expr)}}}`).join(";");
6042
+ const args = entries.flatMap((e) => [
6043
+ JSON.stringify(e.cssKey),
6044
+ e.kind === "literal" ? JSON.stringify(e.value) : wrapIfMultiToken(this.convertExpressionToGo(e.expr))
6045
+ ]);
6046
+ return `{{bf_style_object ${args.join(" ")}}}`;
5913
6047
  }
5914
6048
  renderAttributes(element) {
5915
6049
  const parts = [];
@@ -6032,7 +6166,8 @@ var conformancePins = {
6032
6166
  "filter-nested-find-predicate": [
6033
6167
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
6034
6168
  ],
6035
- "dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2215" }]
6169
+ "dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2215" }],
6170
+ "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2274" }]
6036
6171
  };
6037
6172
  // src/render-divergences.ts
6038
6173
  var renderDivergences = {};