@barefootjs/go-template 0.18.4 → 0.18.5

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
@@ -382,10 +382,11 @@ import {
382
382
  collectModuleStringConsts as collectModuleStringConstsShared,
383
383
  prepareLoweringMatchers,
384
384
  envSignalReaderFor,
385
- computeSsrSeedPlan
385
+ computeSsrSeedPlan,
386
+ isStringConcatBinary
386
387
  } from "@barefootjs/jsx";
387
388
  import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
388
- import { BF_REGION } from "@barefootjs/shared";
389
+ import { BF_REGION, escapeHtml } from "@barefootjs/shared";
389
390
 
390
391
  // src/adapter/lib/go-naming.ts
391
392
  var GO_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -672,6 +673,7 @@ var GO_TEMPLATE_PRIMITIVES = {
672
673
  "Math.floor": { arity: 1, emit: (args) => `bf_floor ${wrapGoArg(args[0])}` },
673
674
  "Math.ceil": { arity: 1, emit: (args) => `bf_ceil ${wrapGoArg(args[0])}` },
674
675
  "Math.round": { arity: 1, emit: (args) => `bf_round ${wrapGoArg(args[0])}` },
676
+ "Math.abs": { arity: 1, emit: (args) => `bf_abs ${wrapGoArg(args[0])}` },
675
677
  "Math.min": { arity: 2, emit: (args) => `bf_min ${wrapGoArg(args[0])} ${wrapGoArg(args[1])}` },
676
678
  "Math.max": { arity: 2, emit: (args) => `bf_max ${wrapGoArg(args[0])} ${wrapGoArg(args[1])}` }
677
679
  };
@@ -697,6 +699,7 @@ class CompileState {
697
699
  hoistedMemoLocals = new Map;
698
700
  loweringMatchers = [];
699
701
  nillablePropNames = new Set;
702
+ stringValueNames = new Set;
700
703
  rootScopeNodes = new Set;
701
704
  memoBackedLoopSlice = new Map;
702
705
  usesHtmlTemplate = false;
@@ -1116,7 +1119,7 @@ function typeInfoToGo(ctx, typeInfo, defaultValue) {
1116
1119
  case "string":
1117
1120
  return "string";
1118
1121
  case "number":
1119
- return "int";
1122
+ return defaultValue !== undefined ? numberPrimitiveGoType(defaultValue) : "int";
1120
1123
  case "boolean":
1121
1124
  return "bool";
1122
1125
  default:
@@ -1167,6 +1170,9 @@ function tsTypeStringToGo(ctx, tsType) {
1167
1170
  return t;
1168
1171
  return "interface{}";
1169
1172
  }
1173
+ function numberPrimitiveGoType(value) {
1174
+ return /^-?\d+\.\d+$/.test(value) ? "float64" : "int";
1175
+ }
1170
1176
  function inferTypeFromValue(value) {
1171
1177
  if (value === "true" || value === "false")
1172
1178
  return "bool";
@@ -1279,9 +1285,9 @@ function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
1279
1285
  return value === "true" ? "true" : "false";
1280
1286
  }
1281
1287
  if (typeInfo.primitive === "number") {
1282
- if (/^\d+$/.test(value))
1288
+ if (/^-?\d+$/.test(value))
1283
1289
  return value;
1284
- if (/^\d+\.\d+$/.test(value))
1290
+ if (/^-?\d+\.\d+$/.test(value))
1285
1291
  return value;
1286
1292
  return "0";
1287
1293
  }
@@ -1306,6 +1312,12 @@ function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
1306
1312
  }
1307
1313
  return '""';
1308
1314
  }
1315
+ if (ctx.state.localStructFields.has(typeInfo.raw)) {
1316
+ const baked = jsLiteralToGo(ctx, typeInfo, preParsed);
1317
+ if (baked !== null)
1318
+ return baked;
1319
+ return `${typeInfo.raw}{}`;
1320
+ }
1309
1321
  }
1310
1322
  return "nil";
1311
1323
  }
@@ -1996,10 +2008,11 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1996
2008
  const operand = String(body.right.value);
1997
2009
  const depName = getterCallName(body.left);
1998
2010
  if (depName) {
1999
- const signal = signals.find((s) => s.getter === depName);
2000
- if (signal) {
2001
- const signalInitial = getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
2002
- return `${signalInitial} ${operator} ${operand}`;
2011
+ const depInitial = resolveGetterValueAsGo(ctx, depName, signals, propsParams, propFallbackVars, resolving);
2012
+ const isArithmeticSafe = depInitial !== null && !depInitial.startsWith("func(");
2013
+ if (isArithmeticSafe) {
2014
+ const wrapped = /\s/.test(depInitial) ? `(${depInitial})` : depInitial;
2015
+ return `${wrapped} ${operator} ${operand}`;
2003
2016
  }
2004
2017
  }
2005
2018
  const propName = propsMemberName(body.left);
@@ -2034,9 +2047,9 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
2034
2047
  }
2035
2048
  const simpleDep = getterCallName(body);
2036
2049
  if (simpleDep) {
2037
- const signal = signals.find((s) => s.getter === simpleDep);
2038
- if (signal) {
2039
- return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
2050
+ const depInitial = resolveGetterValueAsGo(ctx, simpleDep, signals, propsParams, propFallbackVars, resolving);
2051
+ if (depInitial !== null) {
2052
+ return depInitial;
2040
2053
  }
2041
2054
  }
2042
2055
  const simpleProp = propsMemberName(body);
@@ -2427,8 +2440,47 @@ function buildPropTypeOverrides(ctx, ir) {
2427
2440
  }
2428
2441
  }
2429
2442
  }
2443
+ for (const propName of collectToFixedPropNames(ir.root)) {
2444
+ const param = ir.metadata.propsParams.find((p) => p.name === propName);
2445
+ if (!param)
2446
+ continue;
2447
+ const resolved = overrides.get(propName) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
2448
+ if (resolved === "int") {
2449
+ overrides.set(propName, "float64");
2450
+ }
2451
+ }
2430
2452
  return overrides;
2431
2453
  }
2454
+ function collectToFixedPropNames(root) {
2455
+ const names = new Set;
2456
+ const checkExpr = (expr) => {
2457
+ if (expr?.kind === "array-method" && expr.method === "toFixed" && expr.object.kind === "identifier") {
2458
+ names.add(expr.object.name);
2459
+ }
2460
+ };
2461
+ const walk = (node) => {
2462
+ if (!node)
2463
+ return;
2464
+ if (node.type === "expression")
2465
+ checkExpr(node.parsed);
2466
+ if (node.type === "conditional") {
2467
+ checkExpr(node.parsedCondition);
2468
+ walk(node.whenTrue);
2469
+ walk(node.whenFalse);
2470
+ }
2471
+ if (node.type === "element") {
2472
+ for (const attr of node.attrs) {
2473
+ if (attr.value.kind === "expression")
2474
+ checkExpr(attr.value.parsed);
2475
+ }
2476
+ }
2477
+ if ("children" in node && Array.isArray(node.children)) {
2478
+ node.children.forEach(walk);
2479
+ }
2480
+ };
2481
+ walk(root);
2482
+ return names;
2483
+ }
2432
2484
  function resolvePropGoType(ctx, param, propTypeOverrides) {
2433
2485
  const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
2434
2486
  if (param.optional && ctx.state.localStructFields.has(base)) {
@@ -2447,6 +2499,30 @@ function collectNillablePropNames(ctx, ir) {
2447
2499
  return nillable;
2448
2500
  }
2449
2501
 
2502
+ // src/adapter/props/prop-classes.ts
2503
+ function isStringTypeInfo(type) {
2504
+ return type.kind === "primitive" && type.primitive === "string";
2505
+ }
2506
+ function isBareStringLiteral(initialValue) {
2507
+ if (!initialValue)
2508
+ return false;
2509
+ const v = initialValue.trim();
2510
+ return v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"');
2511
+ }
2512
+ function collectStringValueNames(ir) {
2513
+ const names = new Set;
2514
+ for (const s of ir.metadata.signals) {
2515
+ if (isStringTypeInfo(s.type) || isBareStringLiteral(s.initialValue)) {
2516
+ names.add(s.getter);
2517
+ }
2518
+ }
2519
+ for (const p of ir.metadata.propsParams) {
2520
+ if (isStringTypeInfo(p.type))
2521
+ names.add(p.name);
2522
+ }
2523
+ return names;
2524
+ }
2525
+
2450
2526
  // src/adapter/go-template-adapter.ts
2451
2527
  var STRING_METHODS = new Set([
2452
2528
  "replace",
@@ -2489,6 +2565,7 @@ class GoTemplateAdapter extends BaseAdapter {
2489
2565
  }
2490
2566
  inLoop = false;
2491
2567
  loopParamStack = [];
2568
+ loopKeyDepthStack = [];
2492
2569
  loopScalarItemStack = [];
2493
2570
  loopWrapperStack = [];
2494
2571
  loopVarRefCount = new Map;
@@ -2536,6 +2613,7 @@ class GoTemplateAdapter extends BaseAdapter {
2536
2613
  this.state.pendingChildrenDefines = [];
2537
2614
  this.primeCompileState(ir);
2538
2615
  this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
2616
+ this.state.stringValueNames = collectStringValueNames(ir);
2539
2617
  if (!options?.siblingTemplatesRegistered) {
2540
2618
  this.checkImportedLoopChildComponents(ir);
2541
2619
  }
@@ -3233,6 +3311,13 @@ ${goFields.join(`
3233
3311
  break;
3234
3312
  }
3235
3313
  }
3314
+ if (parsedValue) {
3315
+ const goVal = parsedLiteralToGo(this.emitCtx, parsedValue);
3316
+ if (goVal !== null) {
3317
+ emitChildField(prop.name, goVal);
3318
+ break;
3319
+ }
3320
+ }
3236
3321
  const resolvedValue = this.resolveDynamicPropValue(exprText, ir.metadata.signals, ir.metadata.memos, ir.metadata.propsParams);
3237
3322
  if (resolvedValue !== null) {
3238
3323
  emitChildField(prop.name, resolvedValue);
@@ -3240,6 +3325,24 @@ ${goFields.join(`
3240
3325
  break;
3241
3326
  }
3242
3327
  case "jsx-children":
3328
+ if (prop.name !== "children") {
3329
+ const text = this.extractTextChildren(prop.value.children);
3330
+ if (text !== null) {
3331
+ emitChildField(prop.name, JSON.stringify(text));
3332
+ } else {
3333
+ const html = this.extractHtmlChildren(prop.value.children);
3334
+ if (html !== null) {
3335
+ this.state.usesHtmlTemplate = true;
3336
+ emitChildField(prop.name, `template.HTML(${JSON.stringify(html)})`);
3337
+ } else {
3338
+ const scopedHtml = this.extractScopedHtmlChildren(prop.value.children);
3339
+ if (scopedHtml !== null) {
3340
+ this.state.usesHtmlTemplate = true;
3341
+ emitChildField(prop.name, `template.HTML(${scopedHtml})`);
3342
+ }
3343
+ }
3344
+ }
3345
+ }
3243
3346
  break;
3244
3347
  }
3245
3348
  }
@@ -3881,6 +3984,17 @@ ${goFields.join(`
3881
3984
  return computeMemoInitialValueOrNull(this.emitCtx, memo, signals, propsParams, undefined, new Set([memo.name]));
3882
3985
  }
3883
3986
  }
3987
+ const propsObjectName = this.state.propsObjectName;
3988
+ const identifierPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
3989
+ const bareIdentifier = identifierPattern.test(expr) ? expr : null;
3990
+ const barePropAccess = propsObjectName && expr.startsWith(`${propsObjectName}.`) && identifierPattern.test(expr.slice(propsObjectName.length + 1)) ? expr.slice(propsObjectName.length + 1) : null;
3991
+ const passthroughName = bareIdentifier ?? barePropAccess;
3992
+ const localConst = this.state.localConstants.find((c) => c.name === passthroughName);
3993
+ const isPropsDestructureAlias = localConst !== undefined && propsObjectName !== null && localConst.value === `${propsObjectName}.${passthroughName}`;
3994
+ const shadowedByLocal = passthroughName !== null && (localConst !== undefined && !isPropsDestructureAlias || this.state.localHelperNames.has(passthroughName));
3995
+ if (passthroughName && !shadowedByLocal && propsParams.some((p) => p.name === passthroughName)) {
3996
+ return `in.${capitalizeFieldName(passthroughName)}`;
3997
+ }
3884
3998
  return null;
3885
3999
  }
3886
4000
  inferMemoType(memo, signals, propsParamMap) {
@@ -4005,7 +4119,7 @@ ${goFields.join(`
4005
4119
  return this.renderElement(node);
4006
4120
  }
4007
4121
  emitText(node) {
4008
- return node.value;
4122
+ return escapeHtml(node.value);
4009
4123
  }
4010
4124
  emitExpression(node) {
4011
4125
  return this.renderExpression(node);
@@ -4265,7 +4379,7 @@ ${goFields.join(`
4265
4379
  const argsStr = args.map(emit).join(" ");
4266
4380
  return `${calleeStr} ${argsStr}`;
4267
4381
  }
4268
- member(object, property, _computed, emit) {
4382
+ member(object, property, _computed, optional, emit) {
4269
4383
  const objHO = this.higherOrderShapeOf(object);
4270
4384
  if (property === "length" && objHO) {
4271
4385
  const result = this.renderFilterLengthExpr(objHO, emit);
@@ -4307,6 +4421,9 @@ ${goFields.join(`
4307
4421
  const obj = emit(object);
4308
4422
  if (property === "length")
4309
4423
  return `len ${obj}`;
4424
+ if (optional) {
4425
+ return `bf_get ${wrapIfMultiToken(obj)} ${JSON.stringify(goFieldNameForKey(property))}`;
4426
+ }
4310
4427
  return `${obj}.${goFieldNameForKey(property)}`;
4311
4428
  }
4312
4429
  indexAccess(object, index, emit) {
@@ -4337,7 +4454,7 @@ ${goFields.join(`
4337
4454
  case "<=":
4338
4455
  return `le ${wl} ${wr}`;
4339
4456
  case "+":
4340
- return `bf_add ${wl} ${wr}`;
4457
+ return this._emitPlus(left, right, wl, wr);
4341
4458
  case "-":
4342
4459
  return `bf_sub ${wl} ${wr}`;
4343
4460
  case "*":
@@ -4350,6 +4467,15 @@ ${goFields.join(`
4350
4467
  return `${l} ${op} ${r}`;
4351
4468
  }
4352
4469
  }
4470
+ _emitPlus(leftExpr, rightExpr, leftRendered, rightRendered) {
4471
+ if (isStringConcatBinary("+", leftExpr, rightExpr, (n) => this._isStringValueName(n))) {
4472
+ return `bf_concat_str ${leftRendered} ${rightRendered}`;
4473
+ }
4474
+ return `bf_add ${leftRendered} ${rightRendered}`;
4475
+ }
4476
+ _isStringValueName(name) {
4477
+ return this.state.stringValueNames.has(name);
4478
+ }
4353
4479
  unary(op, argument, emit) {
4354
4480
  const arg = emit(argument);
4355
4481
  if (op === "!")
@@ -4561,6 +4687,12 @@ ${goFields.join(`
4561
4687
  const recv = emit(object);
4562
4688
  return `bf_trim ${wrapIfMultiToken(recv)}`;
4563
4689
  }
4690
+ case "trimStart":
4691
+ case "trimEnd": {
4692
+ const fn = method === "trimStart" ? "bf_trim_start" : "bf_trim_end";
4693
+ const recv = emit(object);
4694
+ return `${fn} ${wrapIfMultiToken(recv)}`;
4695
+ }
4564
4696
  case "toFixed": {
4565
4697
  const recv = emit(object);
4566
4698
  const digits = args.length >= 1 ? emit(args[0]) : "0";
@@ -4595,6 +4727,12 @@ ${goFields.join(`
4595
4727
  const newS = emit(args[1]);
4596
4728
  return `bf_replace ${wrapIfMultiToken(recv)} ${wrapIfMultiToken(oldS)} ${wrapIfMultiToken(newS)}`;
4597
4729
  }
4730
+ case "replaceAll": {
4731
+ const recv = emit(object);
4732
+ const oldS = emit(args[0]);
4733
+ const newS = emit(args[1]);
4734
+ return `bf_replace_all ${wrapIfMultiToken(recv)} ${wrapIfMultiToken(oldS)} ${wrapIfMultiToken(newS)}`;
4735
+ }
4598
4736
  case "repeat": {
4599
4737
  const recv = emit(object);
4600
4738
  const count = args.length === 0 ? "0" : emit(args[0]);
@@ -4901,7 +5039,7 @@ ${goFields.join(`
4901
5039
  case "<=":
4902
5040
  return `le ${left} ${right}`;
4903
5041
  case "+":
4904
- return `bf_add ${left} ${right}`;
5042
+ return this._emitPlus(expr.left, expr.right, left, right);
4905
5043
  case "-":
4906
5044
  return `bf_sub ${left} ${right}`;
4907
5045
  case "*":
@@ -5270,7 +5408,7 @@ ${goFields.join(`
5270
5408
  result = `le ${left} ${right}`;
5271
5409
  break;
5272
5410
  case "+":
5273
- result = `bf_add ${left} ${right}`;
5411
+ result = this._emitPlus(expr.left, expr.right, left, right);
5274
5412
  break;
5275
5413
  case "-":
5276
5414
  result = `bf_sub ${left} ${right}`;
@@ -5427,7 +5565,9 @@ ${goFields.join(`
5427
5565
  }
5428
5566
  this.loopScalarItemStack.push(this.scalarLiteralLoopGoType(loop.arrayParsed, loop.itemType) !== null);
5429
5567
  this.loopWrapperStack.push(!!loop.childComponent);
5568
+ this.loopKeyDepthStack.push(loop.depth);
5430
5569
  const children = this.renderChildren(loop.children);
5570
+ this.loopKeyDepthStack.pop();
5431
5571
  this.loopWrapperStack.pop();
5432
5572
  this.loopScalarItemStack.pop();
5433
5573
  const itemMarker = this.loopItemMarker(loop);
@@ -5570,7 +5710,7 @@ ${goFields.join(`
5570
5710
  ${children}`;
5571
5711
  }
5572
5712
  elementAttrEmitter = {
5573
- emitLiteral: (value, name) => `${name}="${value.value}"`,
5713
+ emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
5574
5714
  emitExpression: (value, name) => {
5575
5715
  if (name === "style") {
5576
5716
  const css = this.tryLowerStyleObject(value.expr);
@@ -5660,9 +5800,10 @@ ${children}`;
5660
5800
  let attrName;
5661
5801
  if (attr.name === "className")
5662
5802
  attrName = "class";
5663
- else if (attr.name === "key")
5664
- attrName = "data-key";
5665
- else
5803
+ else if (attr.name === "key") {
5804
+ const depth = this.loopKeyDepthStack.at(-1) ?? 0;
5805
+ attrName = depth > 0 ? `data-key-${depth}` : "data-key";
5806
+ } else
5666
5807
  attrName = attr.name;
5667
5808
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
5668
5809
  if (lowered)
@@ -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,eAqI7B,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,eAgI7B,CAAA"}