@barefootjs/mojolicious 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -884,6 +884,25 @@ function renderSortMethod(recv, c) {
884
884
  });
885
885
  return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
886
886
  }
887
+ function renderReduceMethod(recv, op, direction) {
888
+ const keyEntry = op.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${op.key.field}'`;
889
+ const init = op.type === "string" ? `'${op.init.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'` : op.init;
890
+ return `bf->reduce(${recv}, { op => '${op.op}', ${keyEntry}, type => '${op.type}', init => ${init}, direction => '${direction}' })`;
891
+ }
892
+ function renderFlatMethod(recv, depth) {
893
+ const d = depth === "infinity" ? -1 : depth;
894
+ return `bf->flat(${recv}, ${d})`;
895
+ }
896
+ function renderFlatMapMethod(recv, op) {
897
+ const proj = op.projection;
898
+ if (proj.kind === "tuple") {
899
+ const specs = proj.elements.map((l) => l.kind === "self" ? `['self', '']` : `['field', '${l.field}']`).join(", ");
900
+ return `bf->flat_map_tuple(${recv}, ${specs})`;
901
+ }
902
+ if (proj.kind === "self")
903
+ return `bf->flat_map(${recv}, 'self', '')`;
904
+ return `bf->flat_map(${recv}, 'field', '${proj.field}')`;
905
+ }
887
906
  function isStringTypeInfo(type) {
888
907
  return type?.kind === "primitive" && type.primitive === "string";
889
908
  }
@@ -1008,6 +1027,15 @@ class MojoFilterEmitter {
1008
1027
  sortMethod(_method, object, comparator, emit) {
1009
1028
  return renderSortMethod(emit(object), comparator);
1010
1029
  }
1030
+ reduceMethod(method, object, reduceOp, emit) {
1031
+ return renderReduceMethod(emit(object), reduceOp, method === "reduceRight" ? "right" : "left");
1032
+ }
1033
+ flatMethod(object, depth, emit) {
1034
+ return renderFlatMethod(emit(object), depth);
1035
+ }
1036
+ flatMapMethod(object, op, emit) {
1037
+ return renderFlatMapMethod(emit(object), op);
1038
+ }
1011
1039
  conditional(_test, _consequent, _alternate) {
1012
1040
  return "1";
1013
1041
  }
@@ -1129,6 +1157,15 @@ class MojoTopLevelEmitter {
1129
1157
  sortMethod(_method, object, comparator, emit) {
1130
1158
  return renderSortMethod(emit(object), comparator);
1131
1159
  }
1160
+ reduceMethod(method, object, reduceOp, emit) {
1161
+ return renderReduceMethod(emit(object), reduceOp, method === "reduceRight" ? "right" : "left");
1162
+ }
1163
+ flatMethod(object, depth, emit) {
1164
+ return renderFlatMethod(emit(object), depth);
1165
+ }
1166
+ flatMapMethod(object, op, emit) {
1167
+ return renderFlatMapMethod(emit(object), op);
1168
+ }
1132
1169
  conditional(test, consequent, alternate, emit) {
1133
1170
  return `(${emit(test)} ? ${emit(consequent)} : ${emit(alternate)})`;
1134
1171
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
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
@@ -884,6 +884,25 @@ function renderSortMethod(recv, c) {
884
884
  });
885
885
  return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
886
886
  }
887
+ function renderReduceMethod(recv, op, direction) {
888
+ const keyEntry = op.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${op.key.field}'`;
889
+ const init = op.type === "string" ? `'${op.init.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'` : op.init;
890
+ return `bf->reduce(${recv}, { op => '${op.op}', ${keyEntry}, type => '${op.type}', init => ${init}, direction => '${direction}' })`;
891
+ }
892
+ function renderFlatMethod(recv, depth) {
893
+ const d = depth === "infinity" ? -1 : depth;
894
+ return `bf->flat(${recv}, ${d})`;
895
+ }
896
+ function renderFlatMapMethod(recv, op) {
897
+ const proj = op.projection;
898
+ if (proj.kind === "tuple") {
899
+ const specs = proj.elements.map((l) => l.kind === "self" ? `['self', '']` : `['field', '${l.field}']`).join(", ");
900
+ return `bf->flat_map_tuple(${recv}, ${specs})`;
901
+ }
902
+ if (proj.kind === "self")
903
+ return `bf->flat_map(${recv}, 'self', '')`;
904
+ return `bf->flat_map(${recv}, 'field', '${proj.field}')`;
905
+ }
887
906
  function isStringTypeInfo(type) {
888
907
  return type?.kind === "primitive" && type.primitive === "string";
889
908
  }
@@ -1008,6 +1027,15 @@ class MojoFilterEmitter {
1008
1027
  sortMethod(_method, object, comparator, emit) {
1009
1028
  return renderSortMethod(emit(object), comparator);
1010
1029
  }
1030
+ reduceMethod(method, object, reduceOp, emit) {
1031
+ return renderReduceMethod(emit(object), reduceOp, method === "reduceRight" ? "right" : "left");
1032
+ }
1033
+ flatMethod(object, depth, emit) {
1034
+ return renderFlatMethod(emit(object), depth);
1035
+ }
1036
+ flatMapMethod(object, op, emit) {
1037
+ return renderFlatMapMethod(emit(object), op);
1038
+ }
1011
1039
  conditional(_test, _consequent, _alternate) {
1012
1040
  return "1";
1013
1041
  }
@@ -1129,6 +1157,15 @@ class MojoTopLevelEmitter {
1129
1157
  sortMethod(_method, object, comparator, emit) {
1130
1158
  return renderSortMethod(emit(object), comparator);
1131
1159
  }
1160
+ reduceMethod(method, object, reduceOp, emit) {
1161
+ return renderReduceMethod(emit(object), reduceOp, method === "reduceRight" ? "right" : "left");
1162
+ }
1163
+ flatMethod(object, depth, emit) {
1164
+ return renderFlatMethod(emit(object), depth);
1165
+ }
1166
+ flatMapMethod(object, op, emit) {
1167
+ return renderFlatMapMethod(emit(object), op);
1168
+ }
1132
1169
  conditional(test, consequent, alternate, emit) {
1133
1170
  return `(${emit(test)} ? ${emit(consequent)} : ${emit(alternate)})`;
1134
1171
  }
package/dist/index.js CHANGED
@@ -884,6 +884,25 @@ function renderSortMethod(recv, c) {
884
884
  });
885
885
  return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
886
886
  }
887
+ function renderReduceMethod(recv, op, direction) {
888
+ const keyEntry = op.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${op.key.field}'`;
889
+ const init = op.type === "string" ? `'${op.init.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'` : op.init;
890
+ return `bf->reduce(${recv}, { op => '${op.op}', ${keyEntry}, type => '${op.type}', init => ${init}, direction => '${direction}' })`;
891
+ }
892
+ function renderFlatMethod(recv, depth) {
893
+ const d = depth === "infinity" ? -1 : depth;
894
+ return `bf->flat(${recv}, ${d})`;
895
+ }
896
+ function renderFlatMapMethod(recv, op) {
897
+ const proj = op.projection;
898
+ if (proj.kind === "tuple") {
899
+ const specs = proj.elements.map((l) => l.kind === "self" ? `['self', '']` : `['field', '${l.field}']`).join(", ");
900
+ return `bf->flat_map_tuple(${recv}, ${specs})`;
901
+ }
902
+ if (proj.kind === "self")
903
+ return `bf->flat_map(${recv}, 'self', '')`;
904
+ return `bf->flat_map(${recv}, 'field', '${proj.field}')`;
905
+ }
887
906
  function isStringTypeInfo(type) {
888
907
  return type?.kind === "primitive" && type.primitive === "string";
889
908
  }
@@ -1008,6 +1027,15 @@ class MojoFilterEmitter {
1008
1027
  sortMethod(_method, object, comparator, emit) {
1009
1028
  return renderSortMethod(emit(object), comparator);
1010
1029
  }
1030
+ reduceMethod(method, object, reduceOp, emit) {
1031
+ return renderReduceMethod(emit(object), reduceOp, method === "reduceRight" ? "right" : "left");
1032
+ }
1033
+ flatMethod(object, depth, emit) {
1034
+ return renderFlatMethod(emit(object), depth);
1035
+ }
1036
+ flatMapMethod(object, op, emit) {
1037
+ return renderFlatMapMethod(emit(object), op);
1038
+ }
1011
1039
  conditional(_test, _consequent, _alternate) {
1012
1040
  return "1";
1013
1041
  }
@@ -1129,6 +1157,15 @@ class MojoTopLevelEmitter {
1129
1157
  sortMethod(_method, object, comparator, emit) {
1130
1158
  return renderSortMethod(emit(object), comparator);
1131
1159
  }
1160
+ reduceMethod(method, object, reduceOp, emit) {
1161
+ return renderReduceMethod(emit(object), reduceOp, method === "reduceRight" ? "right" : "left");
1162
+ }
1163
+ flatMethod(object, depth, emit) {
1164
+ return renderFlatMethod(emit(object), depth);
1165
+ }
1166
+ flatMapMethod(object, op, emit) {
1167
+ return renderFlatMapMethod(emit(object), op);
1168
+ }
1132
1169
  conditional(test, consequent, alternate, emit) {
1133
1170
  return `(${emit(test)} ? ${emit(consequent)} : ${emit(alternate)})`;
1134
1171
  }
package/lib/BarefootJS.pm CHANGED
@@ -508,6 +508,71 @@ sub reverse ($self, $recv) {
508
508
  return [ reverse @$recv ];
509
509
  }
510
510
 
511
+ # `Array.prototype.flat(depth?)` (#1448 Tier C) — flatten nested ARRAY
512
+ # refs `$depth` levels deep. A `$depth` of -1 is the `Infinity` sentinel
513
+ # (flatten fully); 0 returns a shallow copy. Non-ARRAY elements are kept
514
+ # as-is (JS only flattens nested arrays). Non-ARRAY receiver → [].
515
+ sub flat ($self, $recv, $depth = 1) {
516
+ return [] unless ref($recv) eq 'ARRAY';
517
+ my @out;
518
+ for my $el (@$recv) {
519
+ if ($depth != 0 && ref($el) eq 'ARRAY') {
520
+ my $next = $depth > 0 ? $depth - 1 : $depth;
521
+ push @out, @{ $self->flat($el, $next) };
522
+ }
523
+ else {
524
+ push @out, $el;
525
+ }
526
+ }
527
+ return \@out;
528
+ }
529
+
530
+ # `Array.prototype.flatMap(fn)` value-returning field projection
531
+ # (#1448 Tier C) — map each element through a self / field projection,
532
+ # then flatten one level. `field` reads a HASH-ref key (the raw JS prop
533
+ # name, as `bf->reduce` does); a projected non-ARRAY value is kept as-is
534
+ # (flatMap = map + flat(1)). Non-ARRAY receiver → [].
535
+ sub flat_map ($self, $recv, $key_kind, $key) {
536
+ return [] unless ref($recv) eq 'ARRAY';
537
+ my @projected;
538
+ for my $el (@$recv) {
539
+ if ($key_kind eq 'field') {
540
+ # JS `i => i.field` on a non-object yields `undefined`, not the
541
+ # element itself — push `undef` so a scalar element doesn't leak
542
+ # into the output (matches Go's `getFieldValue` returning nil).
543
+ push @projected, ref($el) eq 'HASH' ? $el->{$key} : undef;
544
+ }
545
+ else {
546
+ push @projected, $el;
547
+ }
548
+ }
549
+ return $self->flat(\@projected, 1);
550
+ }
551
+
552
+ # `Array.prototype.flatMap(i => [i.a, i.b])` — array-literal tuple
553
+ # projection (#1448 Tier C). Each `@specs` entry is a [kind, key] arrayref
554
+ # (['self', ''] or ['field', 'a']). For each element, every leaf's value
555
+ # is appended in order. flat(1) removes only the literal wrapper, so an
556
+ # array-valued leaf is appended verbatim (no spread) — i.e. just append
557
+ # each leaf. A non-HASH element under a `field` leaf yields undef (JS
558
+ # `i.field` on a non-object). Non-ARRAY receiver → [].
559
+ sub flat_map_tuple ($self, $recv, @specs) {
560
+ return [] unless ref($recv) eq 'ARRAY';
561
+ my @out;
562
+ for my $el (@$recv) {
563
+ for my $spec (@specs) {
564
+ my ($kind, $key) = @$spec;
565
+ if ($kind eq 'field') {
566
+ push @out, ref($el) eq 'HASH' ? $el->{$key} : undef;
567
+ }
568
+ else {
569
+ push @out, $el;
570
+ }
571
+ }
572
+ }
573
+ return \@out;
574
+ }
575
+
511
576
  # `String.prototype.trim()` — strip leading + trailing whitespace.
512
577
  # JS's `String.prototype.trim` matches `\s` in the Unicode sense
513
578
  # (any whitespace including non-breaking space U+00A0); Perl's `\s`
@@ -782,6 +847,63 @@ sub _compare_sort_key ($av, $bv, $compare_type) {
782
847
  return ($av // 0) <=> ($bv // 0); # numeric
783
848
  }
784
849
 
850
+ # Fold an array into a scalar via the arithmetic-fold catalogue
851
+ # (#1448 Tier C). Mirrors Go's `bf_reduce` and JS `reduce(fn, init)` /
852
+ # `reduceRight(fn, init)` for the shapes `(acc, x) => acc <op> x` /
853
+ # `(acc, x) => acc <op> x.field`:
854
+ #
855
+ # bf->reduce($recv, {
856
+ # op => '+' | '*',
857
+ # key_kind => 'self' | 'field',
858
+ # key => '<field>', # when key_kind eq 'field'
859
+ # type => 'numeric' | 'string',
860
+ # init => <seed>, # number, or string for concat
861
+ # direction => 'left' | 'right', # 'right' = reduceRight (default 'left')
862
+ # })
863
+ #
864
+ # Numeric folds accumulate with `+` / `*` (non-numeric keys coalesce to
865
+ # 0); string folds concatenate via `bf->string` (undef → ''). The init
866
+ # seeds the accumulator, so an empty array returns it unchanged — exactly
867
+ # like JS. `direction => 'right'` folds right-to-left (reduceRight); only
868
+ # observable for string concat, since numeric sum / product commute.
869
+ # Float stringification can diverge from Go's for inexact binary
870
+ # fractions (e.g. 0.1 + 0.2); integer sums — the common case — agree.
871
+ sub reduce ($self, $recv, $opts = {}) {
872
+ my $op = $opts->{op} // '+';
873
+ my $key_kind = $opts->{key_kind} // 'self';
874
+ my $key = $opts->{key} // '';
875
+ my $type = $opts->{type} // 'numeric';
876
+ my $direction = $opts->{direction} // 'left';
877
+
878
+ my @items = ref($recv) eq 'ARRAY' ? @$recv : ();
879
+ # reduceRight folds right-to-left; reversing the snapshot keeps the
880
+ # single forward loop below. Only observable for string concat —
881
+ # numeric sum / product commute. Qualify as CORE::reverse — this
882
+ # package defines `sub reverse` (the `.reverse()` helper), so a bare
883
+ # `reverse` is ambiguous under `use warnings`.
884
+ @items = CORE::reverse(@items) if $direction eq 'right';
885
+ my $project = sub ($item) {
886
+ $key_kind eq 'field' && ref($item) eq 'HASH' ? $item->{$key} : $item;
887
+ };
888
+
889
+ if ($type eq 'string') {
890
+ my $acc = $opts->{init} // '';
891
+ $acc .= $self->string($project->($_)) for @items;
892
+ return $acc;
893
+ }
894
+
895
+ my $acc = $opts->{init} // 0;
896
+ for my $item (@items) {
897
+ my $n = $project->($item);
898
+ # Guard `defined` before `looks_like_number` so a missing field
899
+ # (undef) folds as 0 without an "uninitialized value" warning
900
+ # under `use warnings` — matching the `$av // ''` style `sort` uses.
901
+ $n = 0 unless defined $n && looks_like_number($n);
902
+ $op eq '*' ? ($acc *= $n) : ($acc += $n);
903
+ }
904
+ return $acc;
905
+ }
906
+
785
907
  # ---------------------------------------------------------------------------
786
908
  # JSX intrinsic-element spread (#1407)
787
909
  # ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/mojolicious",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
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,14 +52,14 @@
52
52
  "directory": "packages/adapter-mojolicious"
53
53
  },
54
54
  "dependencies": {
55
- "@barefootjs/shared": "0.6.0"
55
+ "@barefootjs/shared": "0.6.1"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@barefootjs/jsx": ">=0.2.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@barefootjs/adapter-tests": "0.1.0",
62
- "@barefootjs/jsx": "0.6.0",
62
+ "@barefootjs/jsx": "0.6.1",
63
63
  "typescript": "^5.0.0"
64
64
  }
65
65
  }
@@ -395,9 +395,16 @@ export function C() {
395
395
  // The nested-higher-order-in-filter-predicate shape also lowers
396
396
  // now (#1443 PR4) — moved to a positive-output test below.
397
397
  const cases: { name: string; body: string; needle: string }[] = [
398
- { name: 'reduce', body: `<div>{items().reduce((s, x) => s + x, 0)}</div>`, needle: '.reduce(' },
398
+ // The arithmetic-fold `.reduce(fn, init)` catalogue now lowers
399
+ // (positive-output tests below + the reduce-* conformance
400
+ // fixtures); the no-init form stays refused — JS throws on an
401
+ // empty array there, which a template can't mirror.
402
+ { name: 'reduce (no init)', body: `<div>{items().reduce((s, x) => s + x)}</div>`, needle: '.reduce(' },
399
403
  { name: 'forEach', body: `<ul>{items().forEach(x => x)}</ul>`, needle: '.forEach(' },
400
- { name: 'flatMap', body: `<ul>{items().flatMap(x => x.tags).map(t => <li key={t}>{t}</li>)}</ul>`, needle: '.flatMap(' },
404
+ // Self / field / field-tuple `.flatMap` now lowers (#1448 Tier C)
405
+ // even as a loop base — so those moved to positive tests below. A
406
+ // tuple with a non-leaf element (a string literal) stays refused.
407
+ { name: 'flatMap (literal element)', body: `<div>{items().flatMap(x => [x.tag, "x"])}</div>`, needle: '.flatMap(' },
401
408
  ]
402
409
 
403
410
  for (const { name, body, needle } of cases) {
@@ -960,6 +967,12 @@ import { fixture as arrayToSortedFixture } from '../../../adapter-tests/fixtures
960
967
  import { fixture as arrayEntriesFixture } from '../../../adapter-tests/fixtures/methods/array-entries'
961
968
  import { fixture as arrayKeysFixture } from '../../../adapter-tests/fixtures/methods/array-keys'
962
969
  import { fixture as arrayValuesFixture } from '../../../adapter-tests/fixtures/methods/array-values'
970
+ // #1448 Tier C — .reduce(fn, init) arithmetic-fold catalogue.
971
+ import { fixture as reduceSumFieldFixture } from '../../../adapter-tests/fixtures/methods/reduce-sum-field'
972
+ import { fixture as reduceSumSelfFixture } from '../../../adapter-tests/fixtures/methods/reduce-sum-self'
973
+ import { fixture as reduceConcatFixture } from '../../../adapter-tests/fixtures/methods/reduce-concat'
974
+ import { fixture as reduceProductFixture } from '../../../adapter-tests/fixtures/methods/reduce-product'
975
+ import { fixture as reduceRightConcatFixture } from '../../../adapter-tests/fixtures/methods/reduce-right-concat'
963
976
 
964
977
  describe('MojoAdapter - #1448 Tier A/B fixture-driven lowering pins', () => {
965
978
  const cases = [
@@ -1020,6 +1033,17 @@ describe('MojoAdapter - #1448 Tier A/B fixture-driven lowering pins', () => {
1020
1033
  { fixture: arrayKeysFixture, expect: '% for my $k (0..$#{$items})' },
1021
1034
  // .values() → standard for loop (same as plain .map())
1022
1035
  { fixture: arrayValuesFixture, expect: '% my $v = $items->[$_i];' },
1036
+ // #1448 Tier C — .reduce(fn, init) arithmetic fold. The structured
1037
+ // ReduceOp lowers to a single `bf->reduce(...)` call with op /
1038
+ // key / type / init / direction in the options hash. Each shape
1039
+ // exercises one arm: field-numeric sum, self-numeric sum,
1040
+ // string-concat fold, the product (`*`) operator, and the
1041
+ // right-to-left `direction` of reduceRight.
1042
+ { fixture: reduceSumFieldFixture, expect: `bf->reduce($items, { op => '+', key_kind => 'field', key => 'duration', type => 'numeric', init => 0, direction => 'left' })` },
1043
+ { fixture: reduceSumSelfFixture, expect: `bf->reduce($nums, { op => '+', key_kind => 'self', type => 'numeric', init => 0, direction => 'left' })` },
1044
+ { fixture: reduceConcatFixture, expect: `bf->reduce($items, { op => '+', key_kind => 'field', key => 'label', type => 'string', init => '', direction => 'left' })` },
1045
+ { fixture: reduceProductFixture, expect: `bf->reduce($items, { op => '*', key_kind => 'field', key => 'qty', type => 'numeric', init => 1, direction => 'left' })` },
1046
+ { fixture: reduceRightConcatFixture, expect: `bf->reduce($items, { op => '+', key_kind => 'field', key => 'label', type => 'string', init => '', direction => 'right' })` },
1023
1047
  ]
1024
1048
 
1025
1049
  for (const { fixture, expect: expectedHelper } of cases) {
@@ -1041,6 +1065,73 @@ describe('MojoAdapter - #1448 Tier A/B fixture-driven lowering pins', () => {
1041
1065
  }
1042
1066
  })
1043
1067
 
1068
+ describe('MojoAdapter - #1448 Tier C .flat(depth?)', () => {
1069
+ function emitFlat(expr: string): string {
1070
+ const a = new MojoAdapter()
1071
+ const ir = compileToIR(`
1072
+ function C({ rows }: { rows: number[][] }) {
1073
+ return <div>{${expr}}</div>
1074
+ }
1075
+ export { C }
1076
+ `, a)
1077
+ return a.generate(ir).template ?? ''
1078
+ }
1079
+
1080
+ test('.flat() emits bf->flat with default depth 1', () => {
1081
+ expect(emitFlat('rows.flat()')).toContain('bf->flat($rows, 1)')
1082
+ })
1083
+
1084
+ test('.flat(2) emits the explicit depth', () => {
1085
+ expect(emitFlat('rows.flat(2)')).toContain('bf->flat($rows, 2)')
1086
+ })
1087
+
1088
+ test('.flat(Infinity) emits the -1 full-depth sentinel', () => {
1089
+ expect(emitFlat('rows.flat(Infinity)')).toContain('bf->flat($rows, -1)')
1090
+ })
1091
+ })
1092
+
1093
+ describe('MojoAdapter - #1448 Tier C .flatMap(field projection)', () => {
1094
+ function emitFlatMap(expr: string): string {
1095
+ const a = new MojoAdapter()
1096
+ const ir = compileToIR(`
1097
+ function C({ rows }: { rows: { a: string; b: string; tags: string[] }[] }) {
1098
+ return <div>{${expr}}</div>
1099
+ }
1100
+ export { C }
1101
+ `, a)
1102
+ return a.generate(ir).template ?? ''
1103
+ }
1104
+
1105
+ test('.flatMap(i => i.field) emits bf->flat_map with the raw field key', () => {
1106
+ expect(emitFlatMap('rows.flatMap(i => i.tags).join(" ")')).toContain(`bf->flat_map($rows, 'field', 'tags')`)
1107
+ })
1108
+
1109
+ test('.flatMap(i => i) emits the self projection', () => {
1110
+ expect(emitFlatMap('rows.flatMap(i => i).join(" ")')).toContain(`bf->flat_map($rows, 'self', '')`)
1111
+ })
1112
+
1113
+ test('.flatMap(i => [i.a, i.b]) emits bf->flat_map_tuple with leaf specs', () => {
1114
+ expect(emitFlatMap('rows.flatMap(i => [i.a, i.b]).join(" ")')).toContain(`bf->flat_map_tuple($rows, ['field', 'a'], ['field', 'b'])`)
1115
+ })
1116
+
1117
+ test('tuple self + field leaves', () => {
1118
+ expect(emitFlatMap('rows.flatMap(i => [i, i.a]).join(" ")')).toContain(`bf->flat_map_tuple($rows, ['self', ''], ['field', 'a'])`)
1119
+ })
1120
+
1121
+ test('field-projection flatMap as a loop base lowers (no BF101)', () => {
1122
+ const a = new MojoAdapter()
1123
+ const ir = compileToIR(`'use client'
1124
+ import { createSignal } from '@barefootjs/client'
1125
+ export function C() {
1126
+ const [items] = createSignal<{ tags: string[] }[]>([])
1127
+ return <ul>{items().flatMap(x => x.tags).map(t => <li key={t}>{t}</li>)}</ul>
1128
+ }`, a)
1129
+ const template = a.generate(ir).template ?? ''
1130
+ expect((a.errors ?? []).filter(e => e.code === 'BF101')).toEqual([])
1131
+ expect(template).toContain(`bf->flat_map($items, 'field', 'tags')`)
1132
+ })
1133
+ })
1134
+
1044
1135
  // =============================================================================
1045
1136
  // #1448 — `/* @client */` escape hatch for STILL-UNSUPPORTED methods
1046
1137
  // =============================================================================
@@ -1099,10 +1190,14 @@ export function C() {
1099
1190
  // Perl fragment that must NOT survive into the template (the pre-fix
1100
1191
  // silent-footgun output for the string rows).
1101
1192
  const unsupported: Array<{ name: string; expr: string; badEmit: string }> = [
1102
- // Tier C array methods.
1103
- { name: 'reduce', expr: `items().reduce((a, b) => a + b.n, 0)`, badEmit: '->{reduce}' },
1104
- { name: 'flatMap', expr: `items().flatMap(i => i.tags)`, badEmit: '->{flatMap}' },
1105
- { name: 'flat', expr: `items().flat()`, badEmit: '->{flat}' },
1193
+ // Tier C array methods. The arithmetic-fold `.reduce(fn, init)`
1194
+ // catalogue now lowers (pinned in the positive reduce-* fixtures);
1195
+ // the no-initial-value form stays refused JS throws on an empty
1196
+ // array there, which a template can't mirror.
1197
+ { name: 'reduce (no init)', expr: `items().reduce((a, b) => a + b.n)`, badEmit: '->{reduce}' },
1198
+ // Self / field / field-tuple `.flatMap` now lowers (#1448 Tier C); a
1199
+ // tuple with a non-leaf element (here a string literal) stays refused.
1200
+ { name: 'flatMap (literal element)', expr: `items().flatMap(i => [i.name, "x"])`, badEmit: '->{flatMap}' },
1106
1201
  // Lowered methods whose MEANINGFUL extra argument isn't lowered yet
1107
1202
  // (#1448): the `fromIndex` of `.includes`/`.indexOf`/`.lastIndexOf`
1108
1203
  // and the variadic `.concat`. The parser refuses these (silently
@@ -56,7 +56,7 @@ import { isAriaBooleanAttr, isBooleanResultExpr } from './boolean-result'
56
56
  * the `IRNodeEmitter` interface.
57
57
  */
58
58
  type MojoRenderCtx = Record<string, never>
59
- import type { ParsedExpr, ParsedStatement, SortComparator, TemplatePart } from '@barefootjs/jsx'
59
+ import type { ParsedExpr, ParsedStatement, SortComparator, ReduceOp, FlatDepth, FlatMapOp, TemplatePart } from '@barefootjs/jsx'
60
60
  import { BF_SLOT, BF_COND } from '@barefootjs/shared'
61
61
 
62
62
  interface PrimitiveSpec {
@@ -1533,6 +1533,64 @@ function renderSortMethod(recv: string, c: SortComparator): string {
1533
1533
  return `bf->sort(${recv}, { keys => [${keyHashes.join(', ')}] })`
1534
1534
  }
1535
1535
 
1536
+ /**
1537
+ * Render a `.reduce(fn, init)` arithmetic fold (#1448 Tier C) as a
1538
+ * `bf->reduce(...)` call. The structured `ReduceOp` maps to the Perl
1539
+ * helper's options hash:
1540
+ *
1541
+ * bf->reduce($recv, { op => '+', key_kind => 'field', key => 'duration',
1542
+ * type => 'numeric', init => 0 })
1543
+ *
1544
+ * A numeric init passes through as a bare Perl number (`0`, `-1`); a
1545
+ * string init (concat fold) is re-quoted from its literal contents.
1546
+ */
1547
+ function renderReduceMethod(recv: string, op: ReduceOp, direction: 'left' | 'right'): string {
1548
+ const keyEntry =
1549
+ op.key.kind === 'self'
1550
+ ? `key_kind => 'self'`
1551
+ : `key_kind => 'field', key => '${op.key.field}'`
1552
+ // `op.init` is the decoded seed value. A numeric seed is already a
1553
+ // canonical decimal literal Perl reads directly; a concat seed is the
1554
+ // string contents, embedded in a single-quoted Perl literal. The `'`
1555
+ // escape is REQUIRED: a seed decoded from a double-quoted JS literal
1556
+ // (e.g. `"a'b"`) is escape-free yet contains an apostrophe. A literal
1557
+ // backslash can't occur (it would need a `\\` escape, which the parser
1558
+ // refuses), but escaping it too keeps this self-contained.
1559
+ const init =
1560
+ op.type === 'string'
1561
+ ? `'${op.init.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`
1562
+ : op.init
1563
+ // `direction` is "left" (reduce) or "right" (reduceRight); the Perl
1564
+ // helper reverses the list for "right". Only observable for concat.
1565
+ return `bf->reduce(${recv}, { op => '${op.op}', ${keyEntry}, type => '${op.type}', init => ${init}, direction => '${direction}' })`
1566
+ }
1567
+
1568
+ // `.flat(depth?)` → `bf->flat($recv, $depth)`. The `Infinity` form lowers
1569
+ // to the `-1` sentinel (flatten fully); a finite depth flattens that many
1570
+ // levels (`0` = shallow copy). See `sub flat` in BarefootJS.pm. (#1448)
1571
+ function renderFlatMethod(recv: string, depth: FlatDepth): string {
1572
+ const d = depth === 'infinity' ? -1 : depth
1573
+ return `bf->flat(${recv}, ${d})`
1574
+ }
1575
+
1576
+ // `.flatMap(i => i)` / `.flatMap(i => i.field)` → `bf->flat_map($recv,
1577
+ // 'self'|'field', 'field')`, and the array-literal tuple form
1578
+ // `i => [i.a, i.b]` → `bf->flat_map_tuple($recv, ['field','a'], ...)`
1579
+ // (one arrayref per leaf). The field key is the raw JS prop name (Perl
1580
+ // hashes are keyed by it), mirroring `bf->reduce`. See `sub flat_map` /
1581
+ // `sub flat_map_tuple` in BarefootJS.pm.
1582
+ function renderFlatMapMethod(recv: string, op: FlatMapOp): string {
1583
+ const proj = op.projection
1584
+ if (proj.kind === 'tuple') {
1585
+ const specs = proj.elements
1586
+ .map(l => (l.kind === 'self' ? `['self', '']` : `['field', '${l.field}']`))
1587
+ .join(', ')
1588
+ return `bf->flat_map_tuple(${recv}, ${specs})`
1589
+ }
1590
+ if (proj.kind === 'self') return `bf->flat_map(${recv}, 'self', '')`
1591
+ return `bf->flat_map(${recv}, 'field', '${proj.field}')`
1592
+ }
1593
+
1536
1594
  /** True when `type` is the `string` primitive. */
1537
1595
  function isStringTypeInfo(type: TypeInfo | undefined): boolean {
1538
1596
  return type?.kind === 'primitive' && type.primitive === 'string'
@@ -1715,6 +1773,18 @@ class MojoFilterEmitter implements ParsedExprEmitter {
1715
1773
  return renderSortMethod(emit(object), comparator)
1716
1774
  }
1717
1775
 
1776
+ reduceMethod(method: 'reduce' | 'reduceRight', object: ParsedExpr, reduceOp: ReduceOp, emit: (e: ParsedExpr) => string): string {
1777
+ return renderReduceMethod(emit(object), reduceOp, method === 'reduceRight' ? 'right' : 'left')
1778
+ }
1779
+
1780
+ flatMethod(object: ParsedExpr, depth: FlatDepth, emit: (e: ParsedExpr) => string): string {
1781
+ return renderFlatMethod(emit(object), depth)
1782
+ }
1783
+
1784
+ flatMapMethod(object: ParsedExpr, op: FlatMapOp, emit: (e: ParsedExpr) => string): string {
1785
+ return renderFlatMapMethod(emit(object), op)
1786
+ }
1787
+
1718
1788
  conditional(_test: ParsedExpr, _consequent: ParsedExpr, _alternate: ParsedExpr): string {
1719
1789
  return '1'
1720
1790
  }
@@ -1896,6 +1966,18 @@ class MojoTopLevelEmitter implements ParsedExprEmitter {
1896
1966
  return renderSortMethod(emit(object), comparator)
1897
1967
  }
1898
1968
 
1969
+ reduceMethod(method: 'reduce' | 'reduceRight', object: ParsedExpr, reduceOp: ReduceOp, emit: (e: ParsedExpr) => string): string {
1970
+ return renderReduceMethod(emit(object), reduceOp, method === 'reduceRight' ? 'right' : 'left')
1971
+ }
1972
+
1973
+ flatMethod(object: ParsedExpr, depth: FlatDepth, emit: (e: ParsedExpr) => string): string {
1974
+ return renderFlatMethod(emit(object), depth)
1975
+ }
1976
+
1977
+ flatMapMethod(object: ParsedExpr, op: FlatMapOp, emit: (e: ParsedExpr) => string): string {
1978
+ return renderFlatMapMethod(emit(object), op)
1979
+ }
1980
+
1899
1981
  conditional(
1900
1982
  test: ParsedExpr,
1901
1983
  consequent: ParsedExpr,