@barefootjs/mojolicious 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.
@@ -1 +1 @@
1
- {"version":3,"file":"array-method.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/array-method.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAA;AAExB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CA6MR;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAOf;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,GAAG,OAAO,EAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAmBf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,EAC/B,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAMf;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAaxE;AAKD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,EACvC,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAoBR"}
1
+ {"version":3,"file":"array-method.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/array-method.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAA;AAExB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,EAAE,EAClB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAgOR;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAOf;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,GAAG,OAAO,EAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAmBf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,EAC/B,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAMf;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,MAAM,CAaxE;AAKD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,EACvC,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAC9B,MAAM,CAoBR"}
@@ -20,7 +20,7 @@ export declare class MojoFilterEmitter implements ParsedExprEmitter {
20
20
  constructor(param: string, localVarMap: Map<string, string>, isStringName?: (n: string) => boolean, onUnsupported?: ((message: string, reason?: string) => void) | undefined);
21
21
  identifier(name: string): string;
22
22
  literal(value: string | number | boolean | null, literalType: LiteralType): string;
23
- member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
23
+ member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string;
24
24
  indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string;
25
25
  call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
26
26
  unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string;
@@ -57,7 +57,7 @@ export declare class MojoTopLevelEmitter implements ParsedExprEmitter {
57
57
  constructor(ctx: MojoEmitContext);
58
58
  identifier(name: string): string;
59
59
  literal(value: string | number | boolean | null, literalType: LiteralType): string;
60
- member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string;
60
+ member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string;
61
61
  indexAccess(object: ParsedExpr, index: ParsedExpr, emit: (e: ParsedExpr) => string): string;
62
62
  call(callee: ParsedExpr, args: ParsedExpr[], emit: (e: ParsedExpr) => string): string;
63
63
  unary(op: string, argument: ParsedExpr, emit: (e: ParsedExpr) => string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"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"}
@@ -76,7 +76,7 @@ function isAriaBooleanAttr(name) {
76
76
  }
77
77
 
78
78
  // src/adapter/mojo-adapter.ts
79
- import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
79
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
80
80
 
81
81
  // src/adapter/lib/constants.ts
82
82
  var MOJO_TEMPLATE_PRIMITIVES = {
@@ -85,7 +85,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
85
85
  Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
86
86
  "Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
87
87
  "Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
88
- "Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
88
+ "Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
89
+ "Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
90
+ "Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
91
+ "Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
89
92
  };
90
93
  var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
91
94
 
@@ -190,6 +193,12 @@ function renderArrayMethod(method, object, args, emit) {
190
193
  const recv = emit(object);
191
194
  return `bf->trim(${recv})`;
192
195
  }
196
+ case "trimStart":
197
+ case "trimEnd": {
198
+ const fn = method === "trimStart" ? "trim_start" : "trim_end";
199
+ const recv = emit(object);
200
+ return `bf->${fn}(${recv})`;
201
+ }
193
202
  case "toFixed": {
194
203
  const recv = emit(object);
195
204
  const digits = args.length >= 1 ? emit(args[0]) : "0";
@@ -223,6 +232,12 @@ function renderArrayMethod(method, object, args, emit) {
223
232
  const newS = emit(args[1]);
224
233
  return `bf->replace(${recv}, ${oldS}, ${newS})`;
225
234
  }
235
+ case "replaceAll": {
236
+ const recv = emit(object);
237
+ const oldS = emit(args[0]);
238
+ const newS = emit(args[1]);
239
+ return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
240
+ }
226
241
  case "repeat": {
227
242
  const recv = emit(object);
228
243
  const count = args.length === 0 ? "0" : emit(args[0]);
@@ -381,7 +396,7 @@ class MojoFilterEmitter {
381
396
  return "undef";
382
397
  return String(value);
383
398
  }
384
- member(object, property, _computed, emit) {
399
+ member(object, property, _computed, _optional, emit) {
385
400
  if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
386
401
  return `scalar(@{${emit(object)}})`;
387
402
  }
@@ -516,7 +531,7 @@ class MojoTopLevelEmitter {
516
531
  return "undef";
517
532
  return String(value);
518
533
  }
519
- member(object, property, _computed, emit) {
534
+ member(object, property, _computed, _optional, emit) {
520
535
  if (object.kind === "identifier" && object.name === "props") {
521
536
  return `$${property}`;
522
537
  }
@@ -526,8 +541,13 @@ class MojoTopLevelEmitter {
526
541
  return staticValue;
527
542
  }
528
543
  const obj = emit(object);
529
- if (property === "length")
544
+ if (property === "length") {
545
+ const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
546
+ const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
547
+ if (isStringReceiver)
548
+ return `length(${obj})`;
530
549
  return `scalar(@{${obj}})`;
550
+ }
531
551
  return `${obj}->{${property}}`;
532
552
  }
533
553
  indexAccess(object, index, emit) {
@@ -976,6 +996,7 @@ class MojoAdapter extends BaseAdapter {
976
996
  options;
977
997
  errors = [];
978
998
  inLoop = false;
999
+ currentLoopKeyDepth = 0;
979
1000
  propsObjectName = null;
980
1001
  propsParams = [];
981
1002
  booleanTypedProps = new Set;
@@ -1108,7 +1129,7 @@ class MojoAdapter extends BaseAdapter {
1108
1129
  return this.renderElement(node);
1109
1130
  }
1110
1131
  emitText(node) {
1111
- return node.value;
1132
+ return escapeHtml(node.value);
1112
1133
  }
1113
1134
  emitExpression(node) {
1114
1135
  return this.renderExpression(node);
@@ -1337,13 +1358,16 @@ ${whenTrue}
1337
1358
  }
1338
1359
  const param = loop.param;
1339
1360
  const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
1340
- const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1361
+ const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1341
1362
  for (const n of loopBound) {
1342
1363
  this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1343
1364
  }
1344
1365
  const prevInLoop = this.inLoop;
1345
1366
  this.inLoop = true;
1367
+ const prevLoopKeyDepth = this.currentLoopKeyDepth;
1368
+ this.currentLoopKeyDepth = loop.depth;
1346
1369
  const renderedChildren = this.renderChildren(loop.children);
1370
+ this.currentLoopKeyDepth = prevLoopKeyDepth;
1347
1371
  this.inLoop = prevInLoop;
1348
1372
  const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1349
1373
  ${renderedChildren}` : renderedChildren;
@@ -1377,23 +1401,31 @@ ${renderedChildren}` : renderedChildren;
1377
1401
  }
1378
1402
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1379
1403
  }
1380
- lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
1381
- if (loop.iterationShape !== "keys") {
1382
- if (supportableDestructure) {
1383
- lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
1384
- for (const b of loop.paramBindings ?? []) {
1385
- const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
1386
- if (b.rest?.kind === "object") {
1387
- const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
1388
- lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
1389
- } else if (b.rest?.kind === "array") {
1390
- lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
1391
- } else {
1392
- lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
1404
+ if (loop.objectIteration) {
1405
+ const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
1406
+ lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
1407
+ if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
1408
+ lines.push(`% my $${param} = ${array}->{${keyVar}};`);
1409
+ }
1410
+ } else {
1411
+ lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
1412
+ if (loop.iterationShape !== "keys") {
1413
+ if (supportableDestructure) {
1414
+ lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
1415
+ for (const b of loop.paramBindings ?? []) {
1416
+ const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
1417
+ if (b.rest?.kind === "object") {
1418
+ const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
1419
+ lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
1420
+ } else if (b.rest?.kind === "array") {
1421
+ lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
1422
+ } else {
1423
+ lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
1424
+ }
1393
1425
  }
1426
+ } else {
1427
+ lines.push(`% my $${param} = ${array}->[${indexVar}];`);
1394
1428
  }
1395
- } else {
1396
- lines.push(`% my $${param} = ${array}->[${indexVar}];`);
1397
1429
  }
1398
1430
  }
1399
1431
  if (loop.filterPredicate) {
@@ -1448,9 +1480,20 @@ ${renderedChildren}` : renderedChildren;
1448
1480
  };
1449
1481
  renderComponent(comp) {
1450
1482
  const propParts = [];
1483
+ const namedSlotCaptures = [];
1451
1484
  for (const p of comp.props) {
1452
1485
  if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
1453
1486
  continue;
1487
+ if (p.value.kind === "jsx-children" && p.name !== "children") {
1488
+ const prevInLoop = this.inLoop;
1489
+ this.inLoop = false;
1490
+ const slotBody = this.renderChildren(p.value.children);
1491
+ this.inLoop = prevInLoop;
1492
+ const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
1493
+ namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
1494
+ propParts.push(`${perlHashKey(p.name)} => ${varName}`);
1495
+ continue;
1496
+ }
1454
1497
  const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
1455
1498
  if (lowered)
1456
1499
  propParts.push(lowered);
@@ -1467,9 +1510,9 @@ ${renderedChildren}` : renderedChildren;
1467
1510
  const childrenBody = this.renderChildren(effectiveChildren);
1468
1511
  this.inLoop = prevInLoop;
1469
1512
  const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
1470
- return `<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
1513
+ return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
1471
1514
  }
1472
- return `<%== bf->render_child('${tplName}'${propsStr}) %>`;
1515
+ return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
1473
1516
  }
1474
1517
  childrenCaptureCounter = 0;
1475
1518
  presenceVarCounter = 0;
@@ -1514,7 +1557,7 @@ ${alternate}
1514
1557
  ${children}`;
1515
1558
  }
1516
1559
  elementAttrEmitter = {
1517
- emitLiteral: (value, name) => `${name}="${value.value}"`,
1560
+ emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
1518
1561
  emitExpression: (value, name) => {
1519
1562
  if (name === "style") {
1520
1563
  const css = this.tryLowerStyleObject(value.expr);
@@ -1608,9 +1651,10 @@ ${children}`;
1608
1651
  let attrName;
1609
1652
  if (attr.name === "className")
1610
1653
  attrName = "class";
1611
- else if (attr.name === "key")
1612
- attrName = "data-key";
1613
- else
1654
+ else if (attr.name === "key") {
1655
+ const depth = this.currentLoopKeyDepth;
1656
+ attrName = depth > 0 ? `data-key-${depth}` : "data-key";
1657
+ } else
1614
1658
  attrName = attr.name;
1615
1659
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
1616
1660
  if (lowered)
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAOlE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAG5E,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAUlE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAG5E,CAAA"}
@@ -36,6 +36,14 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
36
36
  private options;
37
37
  private errors;
38
38
  private inLoop;
39
+ /**
40
+ * `IRLoop.depth` of the loop currently being rendered (save/restore
41
+ * around `renderChildren(loop.children)`, mirroring `inLoop` above).
42
+ * `renderAttributes` reads this to derive the `key` → `data-key`/
43
+ * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
44
+ * re-derived here (#2168 nested-loop-outer-binding).
45
+ */
46
+ private currentLoopKeyDepth;
39
47
  /**
40
48
  * SolidJS-style props identifier (`function(props: P)`) and the
41
49
  * analyzer-extracted prop names. Stashed at `generate()` entry so
@@ -1 +1 @@
1
- {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,WAAW,EAEX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,EAMP,yBAAyB,EAE1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAE3B,KAAK,aAAa,EAClB,KAAK,UAAU,EAyBhB,MAAM,iBAAiB,CAAA;AAKxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AA6BnD,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAuCxD,qBAAa,WAAY,SAAQ,WAAY,YAAW,aAAa,CAAC,aAAa,CAAC;IAClF,IAAI,SAAgB;IACpB,SAAS,SAAa;IACtB,qBAAqB,UAAO;IAG5B,kBAAkB,EAAG,cAAc,CAAS;IAE5C;;;;;;;;;;;OAWG;IACH,kBAAkB,EAAE,yBAAyB,CAA0B;IAEvE,OAAO,CAAC,aAAa,CAAa;IAClC;;;;wCAIoC;IACpC,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAAiB;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,iBAAiB,CAAyB;IAClD;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB,CAAyB;IAClD;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAAyB;IACjD;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB,CAAyB;IAEpD;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAAwB;IACjD;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB,CAAiC;IAC3D;;;;;;OAMG;IACH,OAAO,CAAC,cAAc,CAAmC;IACzD;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc,CAAiC;IACvD;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,qBAAqB,CAAyB;IAEtD,YAAY,OAAO,GAAE,kBAAuB,EAM3C;IAED,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,aAAa,CAsFzE;IAGD;;;;;OAKG;IACH;;;;;OAKG;IACH;;;;;OAKG;IACH,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAO3C;IAED;;;;OAIG;IACH,8BAA8B,CAC5B,IAAI,EAAE,MAAM,GACX;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAgBlD;IAED;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,2BAA2B;IAmBnC;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/B;IAMD,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAE1F;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7B;IAED,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAEzC;IAED,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAElG;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAEpF;IAED,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAE9F;IAED,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAE5F;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7B;IAED,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAElG;IAED,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAc5F;IAED,sEAAsE;IACtE,OAAO,CAAC,iBAAiB;IAkBzB;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;OAOG;IACH,OAAO,CAAC,6BAA6B;IAMrC,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAEtF;IAMD,aAAa,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAqCxC;IAMD,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAe3C;IAMD,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAsC7C;IAED,OAAO,CAAC,gBAAgB;IAOxB;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAcnC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+P/B;IAMD;;;;;;;;OAQG;IACH,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CA0CpC;IAED,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA8CzC;IAED,OAAO,CAAC,sBAAsB,CAAI;IAElC;+DAC2D;IAC3D,OAAO,CAAC,kBAAkB,CAAI;IAE9B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,UAAU;IAIT,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAgB1C;IAMD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CA6MlC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAmB3B,8EAA8E;IAC9E,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,gBAAgB;IAiCxB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAIjD;IAED,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvC;IAED,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvC;IAMD;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAkC5B,OAAO,CAAC,iCAAiC;IAmCzC;;;;;;;OAOG;IACH,OAAO,CAAC,gCAAgC;IAmBxC;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,+BAA+B;IA8BvC;;;;;;;OAOG;IACH,OAAO,KAAK,OAAO,GAUlB;IAED;;;;;OAKG;IACH,OAAO,KAAK,SAAS,GAQpB;IAED,sEAAsE;IACtE,OAAO,KAAK,OAAO,GAElB;IAED,OAAO,CAAC,uBAAuB;IAgF/B;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAI9B;;;;OAIG;IACH;4EACwE;IACxE,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,gBAAgB;IAcxB,iFAAiF;IACjF,OAAO,CAAC,2BAA2B;CAGpC;AAED,eAAO,MAAM,WAAW,aAAoB,CAAA"}
1
+ {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
package/dist/build.js CHANGED
@@ -76,7 +76,7 @@ function isAriaBooleanAttr(name) {
76
76
  }
77
77
 
78
78
  // src/adapter/mojo-adapter.ts
79
- import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
79
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
80
80
 
81
81
  // src/adapter/lib/constants.ts
82
82
  var MOJO_TEMPLATE_PRIMITIVES = {
@@ -85,7 +85,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
85
85
  Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
86
86
  "Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
87
87
  "Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
88
- "Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
88
+ "Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
89
+ "Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
90
+ "Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
91
+ "Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
89
92
  };
90
93
  var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
91
94
 
@@ -190,6 +193,12 @@ function renderArrayMethod(method, object, args, emit) {
190
193
  const recv = emit(object);
191
194
  return `bf->trim(${recv})`;
192
195
  }
196
+ case "trimStart":
197
+ case "trimEnd": {
198
+ const fn = method === "trimStart" ? "trim_start" : "trim_end";
199
+ const recv = emit(object);
200
+ return `bf->${fn}(${recv})`;
201
+ }
193
202
  case "toFixed": {
194
203
  const recv = emit(object);
195
204
  const digits = args.length >= 1 ? emit(args[0]) : "0";
@@ -223,6 +232,12 @@ function renderArrayMethod(method, object, args, emit) {
223
232
  const newS = emit(args[1]);
224
233
  return `bf->replace(${recv}, ${oldS}, ${newS})`;
225
234
  }
235
+ case "replaceAll": {
236
+ const recv = emit(object);
237
+ const oldS = emit(args[0]);
238
+ const newS = emit(args[1]);
239
+ return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
240
+ }
226
241
  case "repeat": {
227
242
  const recv = emit(object);
228
243
  const count = args.length === 0 ? "0" : emit(args[0]);
@@ -381,7 +396,7 @@ class MojoFilterEmitter {
381
396
  return "undef";
382
397
  return String(value);
383
398
  }
384
- member(object, property, _computed, emit) {
399
+ member(object, property, _computed, _optional, emit) {
385
400
  if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
386
401
  return `scalar(@{${emit(object)}})`;
387
402
  }
@@ -516,7 +531,7 @@ class MojoTopLevelEmitter {
516
531
  return "undef";
517
532
  return String(value);
518
533
  }
519
- member(object, property, _computed, emit) {
534
+ member(object, property, _computed, _optional, emit) {
520
535
  if (object.kind === "identifier" && object.name === "props") {
521
536
  return `$${property}`;
522
537
  }
@@ -526,8 +541,13 @@ class MojoTopLevelEmitter {
526
541
  return staticValue;
527
542
  }
528
543
  const obj = emit(object);
529
- if (property === "length")
544
+ if (property === "length") {
545
+ const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
546
+ const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
547
+ if (isStringReceiver)
548
+ return `length(${obj})`;
530
549
  return `scalar(@{${obj}})`;
550
+ }
531
551
  return `${obj}->{${property}}`;
532
552
  }
533
553
  indexAccess(object, index, emit) {
@@ -976,6 +996,7 @@ class MojoAdapter extends BaseAdapter {
976
996
  options;
977
997
  errors = [];
978
998
  inLoop = false;
999
+ currentLoopKeyDepth = 0;
979
1000
  propsObjectName = null;
980
1001
  propsParams = [];
981
1002
  booleanTypedProps = new Set;
@@ -1108,7 +1129,7 @@ class MojoAdapter extends BaseAdapter {
1108
1129
  return this.renderElement(node);
1109
1130
  }
1110
1131
  emitText(node) {
1111
- return node.value;
1132
+ return escapeHtml(node.value);
1112
1133
  }
1113
1134
  emitExpression(node) {
1114
1135
  return this.renderExpression(node);
@@ -1337,13 +1358,16 @@ ${whenTrue}
1337
1358
  }
1338
1359
  const param = loop.param;
1339
1360
  const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
1340
- const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1361
+ const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1341
1362
  for (const n of loopBound) {
1342
1363
  this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1343
1364
  }
1344
1365
  const prevInLoop = this.inLoop;
1345
1366
  this.inLoop = true;
1367
+ const prevLoopKeyDepth = this.currentLoopKeyDepth;
1368
+ this.currentLoopKeyDepth = loop.depth;
1346
1369
  const renderedChildren = this.renderChildren(loop.children);
1370
+ this.currentLoopKeyDepth = prevLoopKeyDepth;
1347
1371
  this.inLoop = prevInLoop;
1348
1372
  const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1349
1373
  ${renderedChildren}` : renderedChildren;
@@ -1377,23 +1401,31 @@ ${renderedChildren}` : renderedChildren;
1377
1401
  }
1378
1402
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1379
1403
  }
1380
- lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
1381
- if (loop.iterationShape !== "keys") {
1382
- if (supportableDestructure) {
1383
- lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
1384
- for (const b of loop.paramBindings ?? []) {
1385
- const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
1386
- if (b.rest?.kind === "object") {
1387
- const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
1388
- lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
1389
- } else if (b.rest?.kind === "array") {
1390
- lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
1391
- } else {
1392
- lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
1404
+ if (loop.objectIteration) {
1405
+ const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
1406
+ lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
1407
+ if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
1408
+ lines.push(`% my $${param} = ${array}->{${keyVar}};`);
1409
+ }
1410
+ } else {
1411
+ lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
1412
+ if (loop.iterationShape !== "keys") {
1413
+ if (supportableDestructure) {
1414
+ lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
1415
+ for (const b of loop.paramBindings ?? []) {
1416
+ const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
1417
+ if (b.rest?.kind === "object") {
1418
+ const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
1419
+ lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
1420
+ } else if (b.rest?.kind === "array") {
1421
+ lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
1422
+ } else {
1423
+ lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
1424
+ }
1393
1425
  }
1426
+ } else {
1427
+ lines.push(`% my $${param} = ${array}->[${indexVar}];`);
1394
1428
  }
1395
- } else {
1396
- lines.push(`% my $${param} = ${array}->[${indexVar}];`);
1397
1429
  }
1398
1430
  }
1399
1431
  if (loop.filterPredicate) {
@@ -1448,9 +1480,20 @@ ${renderedChildren}` : renderedChildren;
1448
1480
  };
1449
1481
  renderComponent(comp) {
1450
1482
  const propParts = [];
1483
+ const namedSlotCaptures = [];
1451
1484
  for (const p of comp.props) {
1452
1485
  if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
1453
1486
  continue;
1487
+ if (p.value.kind === "jsx-children" && p.name !== "children") {
1488
+ const prevInLoop = this.inLoop;
1489
+ this.inLoop = false;
1490
+ const slotBody = this.renderChildren(p.value.children);
1491
+ this.inLoop = prevInLoop;
1492
+ const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
1493
+ namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
1494
+ propParts.push(`${perlHashKey(p.name)} => ${varName}`);
1495
+ continue;
1496
+ }
1454
1497
  const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
1455
1498
  if (lowered)
1456
1499
  propParts.push(lowered);
@@ -1467,9 +1510,9 @@ ${renderedChildren}` : renderedChildren;
1467
1510
  const childrenBody = this.renderChildren(effectiveChildren);
1468
1511
  this.inLoop = prevInLoop;
1469
1512
  const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
1470
- return `<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
1513
+ return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
1471
1514
  }
1472
- return `<%== bf->render_child('${tplName}'${propsStr}) %>`;
1515
+ return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
1473
1516
  }
1474
1517
  childrenCaptureCounter = 0;
1475
1518
  presenceVarCounter = 0;
@@ -1514,7 +1557,7 @@ ${alternate}
1514
1557
  ${children}`;
1515
1558
  }
1516
1559
  elementAttrEmitter = {
1517
- emitLiteral: (value, name) => `${name}="${value.value}"`,
1560
+ emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
1518
1561
  emitExpression: (value, name) => {
1519
1562
  if (name === "style") {
1520
1563
  const css = this.tryLowerStyleObject(value.expr);
@@ -1608,9 +1651,10 @@ ${children}`;
1608
1651
  let attrName;
1609
1652
  if (attr.name === "className")
1610
1653
  attrName = "class";
1611
- else if (attr.name === "key")
1612
- attrName = "data-key";
1613
- else
1654
+ else if (attr.name === "key") {
1655
+ const depth = this.currentLoopKeyDepth;
1656
+ attrName = depth > 0 ? `data-key-${depth}` : "data-key";
1657
+ } else
1614
1658
  attrName = attr.name;
1615
1659
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
1616
1660
  if (lowered)
@@ -1 +1 @@
1
- {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA0I7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAqI7B,CAAA"}
package/dist/index.js CHANGED
@@ -76,7 +76,7 @@ function isAriaBooleanAttr(name) {
76
76
  }
77
77
 
78
78
  // src/adapter/mojo-adapter.ts
79
- import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
79
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
80
80
 
81
81
  // src/adapter/lib/constants.ts
82
82
  var MOJO_TEMPLATE_PRIMITIVES = {
@@ -85,7 +85,10 @@ var MOJO_TEMPLATE_PRIMITIVES = {
85
85
  Number: { arity: 1, emit: (args) => `bf->number(${args[0]})` },
86
86
  "Math.floor": { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
87
87
  "Math.ceil": { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
88
- "Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` }
88
+ "Math.round": { arity: 1, emit: (args) => `bf->round(${args[0]})` },
89
+ "Math.min": { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
90
+ "Math.max": { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
91
+ "Math.abs": { arity: 1, emit: (args) => `bf->abs(${args[0]})` }
89
92
  };
90
93
  var MOJO_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(MOJO_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
91
94
 
@@ -190,6 +193,12 @@ function renderArrayMethod(method, object, args, emit) {
190
193
  const recv = emit(object);
191
194
  return `bf->trim(${recv})`;
192
195
  }
196
+ case "trimStart":
197
+ case "trimEnd": {
198
+ const fn = method === "trimStart" ? "trim_start" : "trim_end";
199
+ const recv = emit(object);
200
+ return `bf->${fn}(${recv})`;
201
+ }
193
202
  case "toFixed": {
194
203
  const recv = emit(object);
195
204
  const digits = args.length >= 1 ? emit(args[0]) : "0";
@@ -223,6 +232,12 @@ function renderArrayMethod(method, object, args, emit) {
223
232
  const newS = emit(args[1]);
224
233
  return `bf->replace(${recv}, ${oldS}, ${newS})`;
225
234
  }
235
+ case "replaceAll": {
236
+ const recv = emit(object);
237
+ const oldS = emit(args[0]);
238
+ const newS = emit(args[1]);
239
+ return `bf->replace_all(${recv}, ${oldS}, ${newS})`;
240
+ }
226
241
  case "repeat": {
227
242
  const recv = emit(object);
228
243
  const count = args.length === 0 ? "0" : emit(args[0]);
@@ -381,7 +396,7 @@ class MojoFilterEmitter {
381
396
  return "undef";
382
397
  return String(value);
383
398
  }
384
- member(object, property, _computed, emit) {
399
+ member(object, property, _computed, _optional, emit) {
385
400
  if (property === "length" && (asCallbackMethodCall(object) !== null || object.kind === "array-literal")) {
386
401
  return `scalar(@{${emit(object)}})`;
387
402
  }
@@ -516,7 +531,7 @@ class MojoTopLevelEmitter {
516
531
  return "undef";
517
532
  return String(value);
518
533
  }
519
- member(object, property, _computed, emit) {
534
+ member(object, property, _computed, _optional, emit) {
520
535
  if (object.kind === "identifier" && object.name === "props") {
521
536
  return `$${property}`;
522
537
  }
@@ -526,8 +541,13 @@ class MojoTopLevelEmitter {
526
541
  return staticValue;
527
542
  }
528
543
  const obj = emit(object);
529
- if (property === "length")
544
+ if (property === "length") {
545
+ const isStr = (e) => isStringTypedOperand2(e, (n) => this.ctx._isStringValueName(n));
546
+ const isStringReceiver = isStr(object) || object.kind === "identifier" && this.ctx._isStringValueName(object.name);
547
+ if (isStringReceiver)
548
+ return `length(${obj})`;
530
549
  return `scalar(@{${obj}})`;
550
+ }
531
551
  return `${obj}->{${property}}`;
532
552
  }
533
553
  indexAccess(object, index, emit) {
@@ -976,6 +996,7 @@ class MojoAdapter extends BaseAdapter {
976
996
  options;
977
997
  errors = [];
978
998
  inLoop = false;
999
+ currentLoopKeyDepth = 0;
979
1000
  propsObjectName = null;
980
1001
  propsParams = [];
981
1002
  booleanTypedProps = new Set;
@@ -1108,7 +1129,7 @@ class MojoAdapter extends BaseAdapter {
1108
1129
  return this.renderElement(node);
1109
1130
  }
1110
1131
  emitText(node) {
1111
- return node.value;
1132
+ return escapeHtml(node.value);
1112
1133
  }
1113
1134
  emitExpression(node) {
1114
1135
  return this.renderExpression(node);
@@ -1337,13 +1358,16 @@ ${whenTrue}
1337
1358
  }
1338
1359
  const param = loop.param;
1339
1360
  const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
1340
- const loopBound = loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1361
+ const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1341
1362
  for (const n of loopBound) {
1342
1363
  this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1343
1364
  }
1344
1365
  const prevInLoop = this.inLoop;
1345
1366
  this.inLoop = true;
1367
+ const prevLoopKeyDepth = this.currentLoopKeyDepth;
1368
+ this.currentLoopKeyDepth = loop.depth;
1346
1369
  const renderedChildren = this.renderChildren(loop.children);
1370
+ this.currentLoopKeyDepth = prevLoopKeyDepth;
1347
1371
  this.inLoop = prevInLoop;
1348
1372
  const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1349
1373
  ${renderedChildren}` : renderedChildren;
@@ -1377,23 +1401,31 @@ ${renderedChildren}` : renderedChildren;
1377
1401
  }
1378
1402
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1379
1403
  }
1380
- lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
1381
- if (loop.iterationShape !== "keys") {
1382
- if (supportableDestructure) {
1383
- lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
1384
- for (const b of loop.paramBindings ?? []) {
1385
- const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
1386
- if (b.rest?.kind === "object") {
1387
- const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
1388
- lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
1389
- } else if (b.rest?.kind === "array") {
1390
- lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
1391
- } else {
1392
- lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
1404
+ if (loop.objectIteration) {
1405
+ const keyVar = loop.objectIteration === "values" ? "$__bf_k" : `$${loop.index ?? param}`;
1406
+ lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`);
1407
+ if (loop.objectIteration === "entries" || loop.objectIteration === "values") {
1408
+ lines.push(`% my $${param} = ${array}->{${keyVar}};`);
1409
+ }
1410
+ } else {
1411
+ lines.push(`% for my ${indexVar} (0..$#{${array}}) {`);
1412
+ if (loop.iterationShape !== "keys") {
1413
+ if (supportableDestructure) {
1414
+ lines.push(`% my $__bf_item = ${array}->[${indexVar}];`);
1415
+ for (const b of loop.paramBindings ?? []) {
1416
+ const parent = perlSegmentAccessor("$__bf_item", b.segments ?? []);
1417
+ if (b.rest?.kind === "object") {
1418
+ const exclude = b.rest.exclude.map((k) => perlStringLiteral(k.key)).join(", ");
1419
+ lines.push(`% my $${b.name} = bf->omit(${parent}, [${exclude}]);`);
1420
+ } else if (b.rest?.kind === "array") {
1421
+ lines.push(`% my $${b.name} = bf->slice(${parent}, ${b.rest.from}, undef);`);
1422
+ } else {
1423
+ lines.push(`% my $${b.name} = ${perlSegmentAccessor("$__bf_item", b.segments ?? [])};`);
1424
+ }
1393
1425
  }
1426
+ } else {
1427
+ lines.push(`% my $${param} = ${array}->[${indexVar}];`);
1394
1428
  }
1395
- } else {
1396
- lines.push(`% my $${param} = ${array}->[${indexVar}];`);
1397
1429
  }
1398
1430
  }
1399
1431
  if (loop.filterPredicate) {
@@ -1448,9 +1480,20 @@ ${renderedChildren}` : renderedChildren;
1448
1480
  };
1449
1481
  renderComponent(comp) {
1450
1482
  const propParts = [];
1483
+ const namedSlotCaptures = [];
1451
1484
  for (const p of comp.props) {
1452
1485
  if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
1453
1486
  continue;
1487
+ if (p.value.kind === "jsx-children" && p.name !== "children") {
1488
+ const prevInLoop = this.inLoop;
1489
+ this.inLoop = false;
1490
+ const slotBody = this.renderChildren(p.value.children);
1491
+ this.inLoop = prevInLoop;
1492
+ const varName = `$bf_prop_${this.childrenCaptureCounter++}`;
1493
+ namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`);
1494
+ propParts.push(`${perlHashKey(p.name)} => ${varName}`);
1495
+ continue;
1496
+ }
1454
1497
  const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
1455
1498
  if (lowered)
1456
1499
  propParts.push(lowered);
@@ -1467,9 +1510,9 @@ ${renderedChildren}` : renderedChildren;
1467
1510
  const childrenBody = this.renderChildren(effectiveChildren);
1468
1511
  this.inLoop = prevInLoop;
1469
1512
  const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
1470
- return `<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
1513
+ return `${namedSlotCaptures.join("")}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
1471
1514
  }
1472
- return `<%== bf->render_child('${tplName}'${propsStr}) %>`;
1515
+ return `${namedSlotCaptures.join("")}<%== bf->render_child('${tplName}'${propsStr}) %>`;
1473
1516
  }
1474
1517
  childrenCaptureCounter = 0;
1475
1518
  presenceVarCounter = 0;
@@ -1514,7 +1557,7 @@ ${alternate}
1514
1557
  ${children}`;
1515
1558
  }
1516
1559
  elementAttrEmitter = {
1517
- emitLiteral: (value, name) => `${name}="${value.value}"`,
1560
+ emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
1518
1561
  emitExpression: (value, name) => {
1519
1562
  if (name === "style") {
1520
1563
  const css = this.tryLowerStyleObject(value.expr);
@@ -1608,9 +1651,10 @@ ${children}`;
1608
1651
  let attrName;
1609
1652
  if (attr.name === "className")
1610
1653
  attrName = "class";
1611
- else if (attr.name === "key")
1612
- attrName = "data-key";
1613
- else
1654
+ else if (attr.name === "key") {
1655
+ const depth = this.currentLoopKeyDepth;
1656
+ attrName = depth > 0 ? `data-key-${depth}` : "data-key";
1657
+ } else
1614
1658
  attrName = attr.name;
1615
1659
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
1616
1660
  if (lowered)
@@ -1800,22 +1844,10 @@ var conformancePins = {
1800
1844
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
1801
1845
  ],
1802
1846
  "array-map-function-reference": [{ code: "BF101", severity: "error" }],
1803
- "dangerous-inner-html": [{ code: "BF101", severity: "error" }],
1804
- "string-replaceall": [{ code: "BF101", severity: "error" }]
1847
+ "dangerous-inner-html": [{ code: "BF101", severity: "error" }]
1805
1848
  };
1806
1849
  // src/render-divergences.ts
1807
- var renderDivergences = {
1808
- "string-length-text": "`.length` on a STRING prop diverges (array-length lowering misapplied to a scalar)",
1809
- "number-tofixed": "the literal `¥` in template text reaches the output as U+FFFD — a UTF-8 encoding gap for non-ASCII literal text adjacent to a dynamic slot",
1810
- "html-entity-text": "`&copy;` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes",
1811
- "math-methods": "Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)",
1812
- "static-attr-escape": 'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
1813
- "object-entries-map": "`Object.entries(prop).map(([k, v]) => …)` renders an EMPTY list — the object-shaped prop silently produces zero iterations",
1814
- "nested-loop-outer-binding": "nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`",
1815
- "jsx-element-prop": "a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped",
1816
- "string-slice": "`.slice()` on a STRING misfires through the array slice helper",
1817
- "string-trim-sided": "`.trimStart()` / `.trimEnd()` render empty (no lowering)"
1818
- };
1850
+ var renderDivergences = {};
1819
1851
  export {
1820
1852
  renderDivergences,
1821
1853
  mojoAdapter,
@@ -1 +1 @@
1
- {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAqB/B,CAAA"}
1
+ {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKxE;AAkCD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,OAAO,iBAAiB,EAAE,eAAe,CAAA;IAClD,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAwNjF;AAqWD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAuBT"}
1
+ {"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKxE;AAkCD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,OAAO,iBAAiB,EAAE,eAAe,CAAA;IAClD,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA6NjF;AAqWD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAuBT"}
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Mojo;
2
- our $VERSION = "0.18.3";
2
+ our $VERSION = "0.18.4";
3
3
  use Mojo::Base -base, -signatures;
4
4
 
5
5
  use Mojo::ByteStream qw(b);
@@ -1,5 +1,5 @@
1
1
  package Mojolicious::Plugin::BarefootJS::DevReload;
2
- our $VERSION = "0.18.3";
2
+ our $VERSION = "0.18.4";
3
3
  use Mojo::Base 'Mojolicious::Plugin', -signatures;
4
4
 
5
5
  =head1 NAME
@@ -1,5 +1,5 @@
1
1
  package Mojolicious::Plugin::BarefootJS;
2
- our $VERSION = "0.18.3";
2
+ our $VERSION = "0.18.4";
3
3
  use Mojo::Base 'Mojolicious::Plugin', -signatures;
4
4
 
5
5
  use Mojo::File qw(path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/mojolicious",
3
- "version": "0.18.4",
3
+ "version": "0.18.5",
4
4
  "description": "Mojolicious EP template adapter for BarefootJS - generates .html.ep files from IR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,7 +52,7 @@
52
52
  "directory": "packages/adapter-mojolicious"
53
53
  },
54
54
  "dependencies": {
55
- "@barefootjs/shared": "0.18.4"
55
+ "@barefootjs/shared": "0.18.5"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@barefootjs/jsx": ">=0.2.0",
@@ -60,6 +60,6 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@barefootjs/adapter-tests": "0.1.0",
63
- "@barefootjs/jsx": "0.18.4"
63
+ "@barefootjs/jsx": "0.18.5"
64
64
  }
65
65
  }
@@ -1163,7 +1163,7 @@ export function Foo(props: { v: string }) {
1163
1163
  // list rather than replace.
1164
1164
  const a = new MojoAdapter()
1165
1165
  const keys = Object.keys(a.templatePrimitives ?? {}).sort()
1166
- expect(keys).toEqual(['JSON.stringify', 'Math.ceil', 'Math.floor', 'Math.round', 'Number', 'String'])
1166
+ expect(keys).toEqual(['JSON.stringify', 'Math.abs', 'Math.ceil', 'Math.floor', 'Math.max', 'Math.min', 'Math.round', 'Number', 'String'])
1167
1167
  })
1168
1168
 
1169
1169
  test('unregistered identifier-path callee is NOT accepted', () => {
@@ -1902,3 +1902,23 @@ export function C() {
1902
1902
  expect(deferred.template).not.toContain('data-x')
1903
1903
  })
1904
1904
  })
1905
+
1906
+ describe('MojoAdapter - named-slot capture identifier safety (#2168 jsx-element-prop)', () => {
1907
+ // A JSX-valued prop under a hyphenated name (`data-slot`, a valid JSX
1908
+ // attribute name) must not leak into the `begin %>...<% end` capture
1909
+ // variable — Perl variable tokens can't contain `-`. The capture
1910
+ // variable is purely counter-based (never derived from the prop name);
1911
+ // the hash KEY passed to `render_child` still carries the real name,
1912
+ // quoted via `perlHashKey`.
1913
+ test('a hyphenated prop name does not appear in the capture variable', () => {
1914
+ const { template } = compileAndGenerate(`
1915
+ function Card(props) { return null }
1916
+ export function Parent() {
1917
+ return <Card data-slot={<strong>Title</strong>}>text</Card>
1918
+ }
1919
+ `)
1920
+ expect(template).toContain('<% my $bf_prop_0 = begin %>')
1921
+ expect(template).toContain("'data-slot' => $bf_prop_0")
1922
+ expect(template).not.toContain('$bf_prop_data')
1923
+ })
1924
+ })
@@ -139,6 +139,15 @@ export function renderArrayMethod(
139
139
  const recv = emit(object)
140
140
  return `bf->trim(${recv})`
141
141
  }
142
+ case 'trimStart':
143
+ case 'trimEnd': {
144
+ // `.trimStart()` / `.trimEnd()` — the one-sided siblings of
145
+ // `.trim()` (#2183 follow-up). Dedicated `bf->trim_start` /
146
+ // `bf->trim_end` helpers, not `bf->trim` with a flag.
147
+ const fn = method === 'trimStart' ? 'trim_start' : 'trim_end'
148
+ const recv = emit(object)
149
+ return `bf->${fn}(${recv})`
150
+ }
142
151
  case 'toFixed': {
143
152
  // `.toFixed(digits?)` — Number → fixed-decimal string. `bf->to_fixed`
144
153
  // mirrors JS rounding + zero-padding (default 0 digits). #1897.
@@ -195,6 +204,16 @@ export function renderArrayMethod(
195
204
  const newS = emit(args[1])
196
205
  return `bf->replace(${recv}, ${oldS}, ${newS})`
197
206
  }
207
+ case 'replaceAll': {
208
+ // `.replaceAll(old, new)` — string-pattern form, EVERY occurrence,
209
+ // via the dedicated `bf->replace_all` helper (not `bf->replace`
210
+ // with a flag) — the regex-pattern form is refused upstream at
211
+ // the parser, same as `.replace`. See #2182.
212
+ const recv = emit(object)
213
+ const oldS = emit(args[0])
214
+ const newS = emit(args[1])
215
+ return `bf->replace_all(${recv}, ${oldS}, ${newS})`
216
+ }
198
217
  case 'repeat': {
199
218
  // `.repeat(n)` — string repeated `n` times. The `bf->repeat`
200
219
  // helper wraps Perl's `x` operator with the same negative-count
@@ -105,7 +105,7 @@ export class MojoFilterEmitter implements ParsedExprEmitter {
105
105
  return String(value)
106
106
  }
107
107
 
108
- member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string {
108
+ member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string {
109
109
  // `.length` on a higher-order result (e.g.
110
110
  // `x.tags.filter(t => t.active).length > 0` inside the outer
111
111
  // filter predicate, #1443). The higher-order emit produces an
@@ -322,7 +322,7 @@ export class MojoTopLevelEmitter implements ParsedExprEmitter {
322
322
  return String(value)
323
323
  }
324
324
 
325
- member(object: ParsedExpr, property: string, _computed: boolean, emit: (e: ParsedExpr) => string): string {
325
+ member(object: ParsedExpr, property: string, _computed: boolean, _optional: boolean, emit: (e: ParsedExpr) => string): string {
326
326
  // `props.x` flattens to the bare `$x` the Mojo SSR caller binds each
327
327
  // prop to (props arrive as individual `my $x = ...` vars, not a
328
328
  // `$props` hashref).
@@ -338,7 +338,22 @@ export class MojoTopLevelEmitter implements ParsedExprEmitter {
338
338
  if (staticValue !== null) return staticValue
339
339
  }
340
340
  const obj = emit(object)
341
- if (property === 'length') return `scalar(@{${obj}})`
341
+ if (property === 'length') {
342
+ // `.length` dispatches on receiver type: a STRING receiver needs
343
+ // Perl's scalar `length($x)`, while the array lowering
344
+ // `scalar(@{$x})` dereferences the value as an array ref and
345
+ // returns 0 for a scalar string (the `string-length-text`
346
+ // divergence). The receiver is string-typed when it's a known
347
+ // string prop/getter (`isStringTypedOperand`) or a bare
348
+ // identifier bound to one — the same `_isStringValueName` witness
349
+ // the `eq`/concat lowering already consults.
350
+ const isStr = (e: ParsedExpr) => isStringTypedOperand(e, n => this.ctx._isStringValueName(n))
351
+ const isStringReceiver =
352
+ isStr(object) ||
353
+ (object.kind === 'identifier' && this.ctx._isStringValueName(object.name))
354
+ if (isStringReceiver) return `length(${obj})`
355
+ return `scalar(@{${obj}})`
356
+ }
342
357
  return `${obj}->{${property}}`
343
358
  }
344
359
 
@@ -26,6 +26,9 @@ export const MOJO_TEMPLATE_PRIMITIVES: Record<string, PrimitiveSpec> = {
26
26
  'Math.floor': { arity: 1, emit: (args) => `bf->floor(${args[0]})` },
27
27
  'Math.ceil': { arity: 1, emit: (args) => `bf->ceil(${args[0]})` },
28
28
  'Math.round': { arity: 1, emit: (args) => `bf->round(${args[0]})` },
29
+ 'Math.min': { arity: 2, emit: (args) => `bf->min(${args[0]}, ${args[1]})` },
30
+ 'Math.max': { arity: 2, emit: (args) => `bf->max(${args[0]}, ${args[1]})` },
31
+ 'Math.abs': { arity: 1, emit: (args) => `bf->abs(${args[0]})` },
29
32
  }
30
33
 
31
34
  /**
@@ -62,7 +62,7 @@ import {
62
62
  } from '@barefootjs/jsx'
63
63
  import { isAriaBooleanAttr, isBooleanResultExpr } from './boolean-result.ts'
64
64
  import type { ParsedExpr, LoweringMatcher } from '@barefootjs/jsx'
65
- import { BF_SLOT, BF_COND, BF_REGION } from '@barefootjs/shared'
65
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from '@barefootjs/shared'
66
66
 
67
67
  import type { MojoRenderCtx } from './lib/types.ts'
68
68
  import { MOJO_PRIMITIVE_EMIT_MAP } from './lib/constants.ts'
@@ -165,6 +165,14 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
165
165
  private options: Required<MojoAdapterOptions>
166
166
  private errors: CompilerError[] = []
167
167
  private inLoop: boolean = false
168
+ /**
169
+ * `IRLoop.depth` of the loop currently being rendered (save/restore
170
+ * around `renderChildren(loop.children)`, mirroring `inLoop` above).
171
+ * `renderAttributes` reads this to derive the `key` → `data-key`/
172
+ * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
173
+ * re-derived here (#2168 nested-loop-outer-binding).
174
+ */
175
+ private currentLoopKeyDepth = 0
168
176
  /**
169
177
  * SolidJS-style props identifier (`function(props: P)`) and the
170
178
  * analyzer-extracted prop names. Stashed at `generate()` entry so
@@ -478,7 +486,9 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
478
486
  }
479
487
 
480
488
  emitText(node: IRText): string {
481
- return node.value
489
+ // IRText carries the entity-DECODED value (Phase 1 decodes JSX
490
+ // character references); re-escape for direct HTML emission.
491
+ return escapeHtml(node.value)
482
492
  }
483
493
 
484
494
  emitExpression(node: IRExpression): string {
@@ -846,17 +856,22 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
846
856
  // the whole body (children + key + filter) so a same-named loop variable
847
857
  // isn't replaced by the const literal (#1749 review). Ref-counted for
848
858
  // nested loops; released after the body lines are assembled below.
849
- const loopBound = loop.iterationShape === 'keys'
850
- ? [param]
851
- : supportableDestructure
852
- ? ['__bf_item', ...(loop.paramBindings ?? []).map(b => b.name), loop.index ?? '_i']
853
- : [param, loop.index ?? '_i']
859
+ const loopBound = loop.objectIteration === 'entries'
860
+ ? [param, loop.index ?? '_k']
861
+ : loop.objectIteration === 'keys' || loop.objectIteration === 'values' || loop.iterationShape === 'keys'
862
+ ? [param]
863
+ : supportableDestructure
864
+ ? ['__bf_item', ...(loop.paramBindings ?? []).map(b => b.name), loop.index ?? '_i']
865
+ : [param, loop.index ?? '_i']
854
866
  for (const n of loopBound) {
855
867
  this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1)
856
868
  }
857
869
  const prevInLoop = this.inLoop
858
870
  this.inLoop = true
871
+ const prevLoopKeyDepth = this.currentLoopKeyDepth
872
+ this.currentLoopKeyDepth = loop.depth
859
873
  const renderedChildren = this.renderChildren(loop.children)
874
+ this.currentLoopKeyDepth = prevLoopKeyDepth
860
875
  this.inLoop = prevInLoop
861
876
 
862
877
  // Whole-item conditional (#1665): prepend an always-present
@@ -918,6 +933,21 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
918
933
  }
919
934
  lines.push(`% my $${sortedHoist} = ${sorted};`)
920
935
  }
936
+ if (loop.objectIteration) {
937
+ // `objectIteration` (#2168 object-entries-map): a Perl hash has no
938
+ // native insertion-order guarantee (unlike Ruby's `Hash`/Python's
939
+ // `dict`) — this codebase's own runtime already works around this
940
+ // elsewhere with `sort keys %$hash` (`BarefootJS.pm`'s
941
+ // `spread_attrs`/`_style_to_css`), so this reuses that exact
942
+ // convention for a deterministic, alphabetically-sorted iteration
943
+ // (not JS insertion order — a documented known limitation for
944
+ // out-of-alphabetical-order data, same as Go/Rust/Xslate).
945
+ const keyVar = loop.objectIteration === 'values' ? '$__bf_k' : `$${loop.index ?? param}`
946
+ lines.push(`% for my ${keyVar} (sort keys %{${array}}) {`)
947
+ if (loop.objectIteration === 'entries' || loop.objectIteration === 'values') {
948
+ lines.push(`% my $${param} = ${array}->{${keyVar}};`)
949
+ }
950
+ } else {
921
951
  lines.push(`% for my ${indexVar} (0..$#{${array}}) {`)
922
952
  if (loop.iterationShape !== 'keys') {
923
953
  if (supportableDestructure) {
@@ -951,6 +981,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
951
981
  lines.push(`% my $${param} = ${array}->[${indexVar}];`)
952
982
  }
953
983
  }
984
+ }
954
985
 
955
986
  // Handle filter().map() pattern by wrapping children in if-condition
956
987
  if (loop.filterPredicate) {
@@ -1049,10 +1080,36 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1049
1080
 
1050
1081
  renderComponent(comp: IRComponent): string {
1051
1082
  const propParts: string[] = []
1083
+ // Named JSX-valued props OTHER than the reserved `children`
1084
+ // (`header={<strong>Title</strong>}`, #2168 jsx-element-prop) get the
1085
+ // same `begin %>…<% end` capture as the reserved children slot below,
1086
+ // just keyed by the prop's own name. `render_child` (BarefootJS.pm)
1087
+ // materializes every prop value that's a CODE ref — not only
1088
+ // `children` — into the Mojo::ByteStream the capture block produces,
1089
+ // so the child's read of the slot back out (`<%= $header %>`) sees an
1090
+ // already-safe ByteStream and Mojo::Template's auto-escape passes it
1091
+ // through unescaped, the same way it already does for `children`.
1092
+ const namedSlotCaptures: string[] = []
1052
1093
  for (const p of comp.props) {
1053
1094
  // Skip callback props (onXxx) and `ref` — both are client-only for
1054
1095
  // SSR (Hono renders neither; the client JS wires them at hydration).
1055
1096
  if ((p.name.match(/^on[A-Z]/) || p.name === 'ref') && p.value.kind === 'expression') continue
1097
+ if (p.value.kind === 'jsx-children' && p.name !== 'children') {
1098
+ const prevInLoop = this.inLoop
1099
+ this.inLoop = false
1100
+ const slotBody = this.renderChildren(p.value.children)
1101
+ this.inLoop = prevInLoop
1102
+ // Purely counter-based — NOT derived from `p.name` or `comp.slotId`.
1103
+ // A JSX prop name can contain characters (`data-slot`) that aren't a
1104
+ // valid Perl variable token, and `comp.slotId` alone would collide
1105
+ // across two named-slot props on the same component invocation
1106
+ // (unlike the reserved children slot, there's only ever one of
1107
+ // those per invocation).
1108
+ const varName = `$bf_prop_${this.childrenCaptureCounter++}`
1109
+ namedSlotCaptures.push(`<% my ${varName} = begin %>${slotBody}<% end %>`)
1110
+ propParts.push(`${perlHashKey(p.name)} => ${varName}`)
1111
+ continue
1112
+ }
1056
1113
  const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name)
1057
1114
  if (lowered) propParts.push(lowered)
1058
1115
  }
@@ -1090,9 +1147,9 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1090
1147
  const childrenBody = this.renderChildren(effectiveChildren)
1091
1148
  this.inLoop = prevInLoop
1092
1149
  const varName = `$bf_children_${comp.slotId ?? 'c' + this.childrenCaptureCounter++}`
1093
- return `<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`
1150
+ return `${namedSlotCaptures.join('')}<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`
1094
1151
  }
1095
- return `<%== bf->render_child('${tplName}'${propsStr}) %>`
1152
+ return `${namedSlotCaptures.join('')}<%== bf->render_child('${tplName}'${propsStr}) %>`
1096
1153
  }
1097
1154
 
1098
1155
  private childrenCaptureCounter = 0
@@ -1178,7 +1235,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1178
1235
  * template). Routed through the shared dispatcher (#1290 step 2).
1179
1236
  */
1180
1237
  private readonly elementAttrEmitter: AttrValueEmitter = {
1181
- emitLiteral: (value, name) => `${name}="${value.value}"`,
1238
+ emitLiteral: (value, name) => `${name}="${escapeHtml(value.value)}"`,
1182
1239
  emitExpression: (value, name) => {
1183
1240
  // `style={{ … }}` object literal → a CSS string with dynamic values
1184
1241
  // interpolated, instead of refusing the bare object with BF101 (#1322).
@@ -1440,7 +1497,10 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1440
1497
  // rewrite happens at attribute-emit time.
1441
1498
  let attrName: string
1442
1499
  if (attr.name === 'className') attrName = 'class'
1443
- else if (attr.name === 'key') attrName = 'data-key'
1500
+ else if (attr.name === 'key') {
1501
+ const depth = this.currentLoopKeyDepth
1502
+ attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
1503
+ }
1444
1504
  else attrName = attr.name
1445
1505
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
1446
1506
  if (lowered) parts.push(lowered)
@@ -141,9 +141,4 @@ export const conformancePins: ConformancePins = {
141
141
  // the shape loudly instead of emitting entity-escaped markup that
142
142
  // silently renders tags as text.
143
143
  'dangerous-inner-html': [{ code: 'BF101', severity: 'error' }],
144
- // Edge-case sweep (Priority 12): `.replaceAll` has no lowering yet —
145
- // only first-occurrence `.replace` is wired to the runtime helpers.
146
- // Refused with BF101 rather than reusing the first-only lowering,
147
- // which would silently change semantics.
148
- 'string-replaceall': [{ code: 'BF101', severity: 'error' }],
149
144
  }
@@ -14,25 +14,4 @@
14
14
 
15
15
  import type { RenderDivergences } from '@barefootjs/jsx'
16
16
 
17
- export const renderDivergences: RenderDivergences = {
18
- 'string-length-text':
19
- '`.length` on a STRING prop diverges (array-length lowering misapplied to a scalar)',
20
- 'number-tofixed':
21
- 'the literal `¥` in template text reaches the output as U+FFFD — a UTF-8 encoding gap for non-ASCII literal text adjacent to a dynamic slot',
22
- 'html-entity-text':
23
- '`&copy;` in JSX literal text: Hono decodes to `©`, this adapter re-emits the raw entity — same DOM, different bytes',
24
- 'math-methods':
25
- 'Math.min/max/abs over a signal render empty (only Math.floor is in the template-primitive registry)',
26
- 'static-attr-escape':
27
- 'static attribute values are not HTML-escaped (`title="Fish & Chips"` emitted raw; Hono escapes)',
28
- 'object-entries-map':
29
- '`Object.entries(prop).map(([k, v]) => …)` renders an EMPTY list — the object-shaped prop silently produces zero iterations',
30
- 'nested-loop-outer-binding':
31
- 'nested-loop inner items carry `data-key` where the reference emits the depth-suffixed `data-key-1`',
32
- 'jsx-element-prop':
33
- 'a JSX element passed as a NON-children prop renders an empty slot — the element value is silently dropped',
34
- 'string-slice':
35
- '`.slice()` on a STRING misfires through the array slice helper',
36
- 'string-trim-sided':
37
- '`.trimStart()` / `.trimEnd()` render empty (no lowering)',
38
- }
17
+ export const renderDivergences: RenderDivergences = {}
@@ -240,6 +240,11 @@ export async function renderMojoComponent(options: RenderOptions): Promise<strin
240
240
  use strict;
241
241
  use warnings;
242
242
  use utf8;
243
+ # The template is read through :utf8, so $output holds decoded wide
244
+ # characters; without a matching layer on STDOUT Perl emits them as
245
+ # latin-1 bytes and the harness reads U+FFFD (©/¥ mangling). A real
246
+ # Mojolicious response encodes on the way out — this is harness-only.
247
+ binmode STDOUT, ':encoding(UTF-8)';
243
248
 
244
249
  use lib '${LIB_DIR}', '${PERL_CORE_LIB_DIR}';
245
250
  use Mojolicious;