@barefootjs/mojolicious 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/index.js +6 -2
- package/dist/adapter/mojo-adapter.d.ts +1 -0
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/build.js +6 -2
- package/dist/index.js +6 -2
- package/lib/BarefootJS.pm +56 -29
- package/package.json +3 -3
- package/src/__tests__/mojo-adapter.test.ts +13 -21
- package/src/adapter/mojo-adapter.ts +20 -9
- package/src/test-render.ts +17 -1
package/dist/adapter/index.js
CHANGED
|
@@ -87,6 +87,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
87
87
|
name = "mojolicious";
|
|
88
88
|
extension = ".html.ep";
|
|
89
89
|
templatesPerComponent = true;
|
|
90
|
+
importMapInjection = "html-snippet";
|
|
90
91
|
templatePrimitives = MOJO_PRIMITIVE_EMIT_MAP;
|
|
91
92
|
componentName = "";
|
|
92
93
|
options;
|
|
@@ -919,8 +920,11 @@ function perlIdentifierFromMarkerId(markerId) {
|
|
|
919
920
|
return markerId.replace(/[^a-zA-Z0-9]/g, (ch) => ch === "_" ? "__" : `_x${ch.charCodeAt(0).toString(16)}`);
|
|
920
921
|
}
|
|
921
922
|
function renderSortMethod(recv, c) {
|
|
922
|
-
const
|
|
923
|
-
|
|
923
|
+
const keyHashes = c.keys.map((k) => {
|
|
924
|
+
const keyEntry = k.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${k.key.field}'`;
|
|
925
|
+
return `{ ${keyEntry}, compare_type => '${k.type}', direction => '${k.direction}' }`;
|
|
926
|
+
});
|
|
927
|
+
return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
|
|
924
928
|
}
|
|
925
929
|
|
|
926
930
|
class MojoFilterEmitter {
|
|
@@ -24,6 +24,7 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
24
24
|
name: string;
|
|
25
25
|
extension: string;
|
|
26
26
|
templatesPerComponent: boolean;
|
|
27
|
+
importMapInjection: 'html-snippet';
|
|
27
28
|
/**
|
|
28
29
|
* Identifier-path callees the Mojo runtime can render in template
|
|
29
30
|
* scope. The relocate pass consults this map to mark matching
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,EAKP,yBAAyB,EAC1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAM3B,KAAK,aAAa,EAClB,KAAK,UAAU,EAYhB,MAAM,iBAAiB,CAAA;AAGxB;;;;;;GAMG;AACH,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAiD,MAAM,iBAAiB,CAAA;AAkEhG,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,qBAAa,WAAY,SAAQ,WAAY,YAAW,aAAa,CAAC,aAAa,CAAC;IAClF,IAAI,SAAgB;IACpB,SAAS,SAAa;IACtB,qBAAqB,UAAO;
|
|
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
|
@@ -87,6 +87,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
87
87
|
name = "mojolicious";
|
|
88
88
|
extension = ".html.ep";
|
|
89
89
|
templatesPerComponent = true;
|
|
90
|
+
importMapInjection = "html-snippet";
|
|
90
91
|
templatePrimitives = MOJO_PRIMITIVE_EMIT_MAP;
|
|
91
92
|
componentName = "";
|
|
92
93
|
options;
|
|
@@ -919,8 +920,11 @@ function perlIdentifierFromMarkerId(markerId) {
|
|
|
919
920
|
return markerId.replace(/[^a-zA-Z0-9]/g, (ch) => ch === "_" ? "__" : `_x${ch.charCodeAt(0).toString(16)}`);
|
|
920
921
|
}
|
|
921
922
|
function renderSortMethod(recv, c) {
|
|
922
|
-
const
|
|
923
|
-
|
|
923
|
+
const keyHashes = c.keys.map((k) => {
|
|
924
|
+
const keyEntry = k.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${k.key.field}'`;
|
|
925
|
+
return `{ ${keyEntry}, compare_type => '${k.type}', direction => '${k.direction}' }`;
|
|
926
|
+
});
|
|
927
|
+
return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
|
|
924
928
|
}
|
|
925
929
|
|
|
926
930
|
class MojoFilterEmitter {
|
package/dist/index.js
CHANGED
|
@@ -87,6 +87,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
87
87
|
name = "mojolicious";
|
|
88
88
|
extension = ".html.ep";
|
|
89
89
|
templatesPerComponent = true;
|
|
90
|
+
importMapInjection = "html-snippet";
|
|
90
91
|
templatePrimitives = MOJO_PRIMITIVE_EMIT_MAP;
|
|
91
92
|
componentName = "";
|
|
92
93
|
options;
|
|
@@ -919,8 +920,11 @@ function perlIdentifierFromMarkerId(markerId) {
|
|
|
919
920
|
return markerId.replace(/[^a-zA-Z0-9]/g, (ch) => ch === "_" ? "__" : `_x${ch.charCodeAt(0).toString(16)}`);
|
|
920
921
|
}
|
|
921
922
|
function renderSortMethod(recv, c) {
|
|
922
|
-
const
|
|
923
|
-
|
|
923
|
+
const keyHashes = c.keys.map((k) => {
|
|
924
|
+
const keyEntry = k.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${k.key.field}'`;
|
|
925
|
+
return `{ ${keyEntry}, compare_type => '${k.type}', direction => '${k.direction}' }`;
|
|
926
|
+
});
|
|
927
|
+
return `bf->sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
|
|
924
928
|
}
|
|
925
929
|
|
|
926
930
|
class MojoFilterEmitter {
|
package/lib/BarefootJS.pm
CHANGED
|
@@ -530,7 +530,8 @@ sub trim ($self, $recv) {
|
|
|
530
530
|
# lowering (#1448 Tier B). Non-mutating — JS's mutate-vs-new
|
|
531
531
|
# distinction is moot in SSR template context.
|
|
532
532
|
#
|
|
533
|
-
# Opts hash-ref
|
|
533
|
+
# Opts hash-ref. The compiler emits a `keys` list of per-key hashes
|
|
534
|
+
# in priority order; each hash carries:
|
|
534
535
|
#
|
|
535
536
|
# key_kind => 'self' | 'field'
|
|
536
537
|
# key => '' when key_kind eq 'self'; field name verbatim
|
|
@@ -539,7 +540,7 @@ sub trim ($self, $recv) {
|
|
|
539
540
|
# applied. Perl hash lookups are case-sensitive so
|
|
540
541
|
# the key here must match the actual hash key the
|
|
541
542
|
# user populated.
|
|
542
|
-
# compare_type => 'numeric' | 'string'
|
|
543
|
+
# compare_type => 'numeric' | 'string' | 'auto'
|
|
543
544
|
# direction => 'asc' | 'desc'
|
|
544
545
|
#
|
|
545
546
|
# Accepted comparator catalogue (gated upstream at parse time —
|
|
@@ -548,47 +549,73 @@ sub trim ($self, $recv) {
|
|
|
548
549
|
# (a,b) => a.f - b.f → field, numeric
|
|
549
550
|
# (a,b) => a - b → self, numeric
|
|
550
551
|
# (a,b) => a[.f].localeCompare(b[.f]) → field|self, string
|
|
552
|
+
# (a,b) => a.f > b.f ? 1 : -1 → field|self, auto
|
|
553
|
+
# any of the above ||-chained → multi-key tie-breaks
|
|
551
554
|
# (and reversed-operand variants for `desc`).
|
|
552
555
|
#
|
|
553
|
-
#
|
|
554
|
-
#
|
|
556
|
+
# `auto` (relational-ternary lowering) compares numerically when both
|
|
557
|
+
# keys `looks_like_number`, else lexically — Go's `bf_sort` applies the
|
|
558
|
+
# same rule so the two template adapters stay byte-equal.
|
|
559
|
+
#
|
|
560
|
+
# A future `nulls => 'first' | 'last'` knob can land per key without
|
|
561
|
+
# churn — the opts hash is the right place to grow.
|
|
555
562
|
|
|
556
563
|
sub sort ($self, $recv, $opts = {}) {
|
|
557
564
|
return [] unless ref($recv) eq 'ARRAY';
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
my
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
565
|
+
|
|
566
|
+
# Normalise the per-key specs (priority order, length >= 1).
|
|
567
|
+
my @spec = map {
|
|
568
|
+
{
|
|
569
|
+
key_kind => $_->{key_kind} // 'self',
|
|
570
|
+
key => $_->{key} // '',
|
|
571
|
+
compare_type => $_->{compare_type} // 'numeric',
|
|
572
|
+
direction => $_->{direction} // 'asc',
|
|
573
|
+
}
|
|
574
|
+
} @{ $opts->{keys} // [] };
|
|
575
|
+
return [ @$recv ] unless @spec;
|
|
576
|
+
|
|
577
|
+
# Schwartzian transform: project each item to all its sort keys
|
|
578
|
+
# once, then compare projected keys. Cheaper than re-resolving the
|
|
579
|
+
# field accessors inside every comparison for non-trivial arrays.
|
|
566
580
|
my @keyed = map {
|
|
567
581
|
my $item = $_;
|
|
568
|
-
my
|
|
569
|
-
|
|
582
|
+
my @ks = map {
|
|
583
|
+
$_->{key_kind} eq 'field' && ref($item) eq 'HASH' ? $item->{ $_->{key} } : $item;
|
|
584
|
+
} @spec;
|
|
585
|
+
[ \@ks, $item ];
|
|
570
586
|
} @$recv;
|
|
571
587
|
|
|
572
|
-
my $cmp
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
# ordering); matching Go's `toFloat64(nil) == 0` keeps the
|
|
582
|
-
# adapter outputs symmetric.
|
|
583
|
-
$cmp = $direction eq 'desc'
|
|
584
|
-
? sub { ($b->[0] // 0) <=> ($a->[0] // 0) }
|
|
585
|
-
: sub { ($a->[0] // 0) <=> ($b->[0] // 0) };
|
|
586
|
-
}
|
|
588
|
+
my $cmp = sub {
|
|
589
|
+
for my $i (0 .. $#spec) {
|
|
590
|
+
my $sp = $spec[$i];
|
|
591
|
+
my $c = _compare_sort_key($a->[0][$i], $b->[0][$i], $sp->{compare_type});
|
|
592
|
+
next if $c == 0; # tie on this key — try the next
|
|
593
|
+
return $sp->{direction} eq 'desc' ? -$c : $c;
|
|
594
|
+
}
|
|
595
|
+
return 0;
|
|
596
|
+
};
|
|
587
597
|
|
|
588
598
|
my @sorted = sort $cmp @keyed;
|
|
589
599
|
return [ map { $_->[1] } @sorted ];
|
|
590
600
|
}
|
|
591
601
|
|
|
602
|
+
# Compare two projected keys, ascending orientation (-1 / 0 / 1); the
|
|
603
|
+
# caller negates for 'desc'. 'auto' compares numerically when both
|
|
604
|
+
# keys look like numbers, else lexically (matches Go's `bf_sort`).
|
|
605
|
+
# undef coalesces to '' / 0 so the order stays total without warnings.
|
|
606
|
+
sub _compare_sort_key ($av, $bv, $compare_type) {
|
|
607
|
+
if ($compare_type eq 'string') {
|
|
608
|
+
return ($av // '') cmp ($bv // '');
|
|
609
|
+
}
|
|
610
|
+
if ($compare_type eq 'auto') {
|
|
611
|
+
if (looks_like_number($av // '') && looks_like_number($bv // '')) {
|
|
612
|
+
return ($av // 0) <=> ($bv // 0);
|
|
613
|
+
}
|
|
614
|
+
return ($av // '') cmp ($bv // '');
|
|
615
|
+
}
|
|
616
|
+
return ($av // 0) <=> ($bv // 0); # numeric
|
|
617
|
+
}
|
|
618
|
+
|
|
592
619
|
# ---------------------------------------------------------------------------
|
|
593
620
|
# JSX intrinsic-element spread (#1407)
|
|
594
621
|
# ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/mojolicious",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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.
|
|
55
|
+
"@barefootjs/shared": "0.5.0"
|
|
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.
|
|
62
|
+
"@barefootjs/jsx": "0.5.0",
|
|
63
63
|
"typescript": "^5.0.0"
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -69,21 +69,6 @@ runAdapterConformanceTests({
|
|
|
69
69
|
'toggle-shared',
|
|
70
70
|
'reactive-props',
|
|
71
71
|
'props-reactivity-comparison',
|
|
72
|
-
// #1633: child component forwards a destructured rest bag
|
|
73
|
-
// (`function NativeSelect({ children, ...props })`) onto its
|
|
74
|
-
// element via `{...props}`. The Mojo child template references
|
|
75
|
-
// `$props` (`<%== $bf->spread_attrs($props) %>`), but
|
|
76
|
-
// `render_child` doesn't plumb the rest-spread bag into the child
|
|
77
|
-
// template's scope, so rendering dies with `Global symbol "$props"
|
|
78
|
-
// requires explicit package name`. The Go adapter plumbs the same
|
|
79
|
-
// bag via the `Spread_0`/`Extras map[string]any` Input field, so it
|
|
80
|
-
// renders fine; Mojo needs the equivalent render_child plumbing.
|
|
81
|
-
// The fixture exists to pin the CSR layer of #1633 (children
|
|
82
|
-
// materialization), which Hono / Go / CSR all verify — Mojo's
|
|
83
|
-
// spread-bag-in-child gap is orthogonal. Same class of spread-bag
|
|
84
|
-
// limitation that keeps `jsx-spread-rest-prop` /
|
|
85
|
-
// `jsx-spread-props-object` off the CSR conformance path.
|
|
86
|
-
'native-select-spread-children',
|
|
87
72
|
],
|
|
88
73
|
// Per-fixture build-time contracts for shapes the Mojo adapter
|
|
89
74
|
// intentionally refuses to lower. Owned by this adapter test file
|
|
@@ -909,6 +894,8 @@ import { fixture as arraySortFieldAscFixture } from '../../../adapter-tests/fixt
|
|
|
909
894
|
import { fixture as arraySortFieldDescFixture } from '../../../adapter-tests/fixtures/methods/array-sort-field-desc'
|
|
910
895
|
import { fixture as arraySortPrimitiveFixture } from '../../../adapter-tests/fixtures/methods/array-sort-primitive'
|
|
911
896
|
import { fixture as arraySortLocaleFixture } from '../../../adapter-tests/fixtures/methods/array-sort-locale'
|
|
897
|
+
import { fixture as arraySortMultiKeyFixture } from '../../../adapter-tests/fixtures/methods/array-sort-multikey'
|
|
898
|
+
import { fixture as arraySortTernaryFixture } from '../../../adapter-tests/fixtures/methods/array-sort-ternary'
|
|
912
899
|
import { fixture as arrayToSortedFixture } from '../../../adapter-tests/fixtures/methods/array-toSorted'
|
|
913
900
|
// #1448 Tier B — .entries / .keys / .values iteration shapes.
|
|
914
901
|
import { fixture as arrayEntriesFixture } from '../../../adapter-tests/fixtures/methods/array-entries'
|
|
@@ -933,12 +920,17 @@ describe('MojoAdapter - #1448 Tier A/B fixture-driven lowering pins', () => {
|
|
|
933
920
|
{ fixture: stringTrimFixture, expect: 'bf->trim($value)' },
|
|
934
921
|
// #1448 Tier B — sort / toSorted. The loop-chained field cases
|
|
935
922
|
// hoist into a `my $bf_iter_lN = bf->sort(...)` local; the
|
|
936
|
-
// standalone primitive cases inline the call.
|
|
937
|
-
|
|
938
|
-
{ fixture:
|
|
939
|
-
{ fixture:
|
|
940
|
-
{ fixture:
|
|
941
|
-
{ fixture:
|
|
923
|
+
// standalone primitive cases inline the call. Each comparison key
|
|
924
|
+
// is one hash under `keys` (a single-key comparator → one element).
|
|
925
|
+
{ fixture: arraySortFieldAscFixture, expect: `bf->sort($items, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'asc' }] })` },
|
|
926
|
+
{ fixture: arraySortFieldDescFixture, expect: `bf->sort($items, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'desc' }] })` },
|
|
927
|
+
{ fixture: arraySortPrimitiveFixture, expect: `bf->sort($nums, { keys => [{ key_kind => 'self', compare_type => 'numeric', direction => 'asc' }] })` },
|
|
928
|
+
{ fixture: arraySortLocaleFixture, expect: `bf->sort($names, { keys => [{ key_kind => 'self', compare_type => 'string', direction => 'asc' }] })` },
|
|
929
|
+
// Multi-key (`||`-chain): one hash per comparison key, in order.
|
|
930
|
+
{ fixture: arraySortMultiKeyFixture, expect: `bf->sort($items, { keys => [{ key_kind => 'field', key => 'price', compare_type => 'numeric', direction => 'asc' }, { key_kind => 'field', key => 'name', compare_type => 'string', direction => 'asc' }] })` },
|
|
931
|
+
// Relational-ternary comparator lowers to a single `auto` key.
|
|
932
|
+
{ fixture: arraySortTernaryFixture, expect: `bf->sort($items, { keys => [{ key_kind => 'field', key => 'rank', compare_type => 'auto', direction => 'asc' }] })` },
|
|
933
|
+
{ fixture: arrayToSortedFixture, expect: `bf->sort($nums, { keys => [{ key_kind => 'self', compare_type => 'numeric', direction => 'asc' }] })` },
|
|
942
934
|
// #1448 Tier B — iteration shapes. These are loop-level patterns.
|
|
943
935
|
// .entries() → for loop with both $i index var and $v value var
|
|
944
936
|
{ fixture: arrayEntriesFixture, expect: '% my $v = $items->[$i];' },
|
|
@@ -135,6 +135,9 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
|
|
|
135
135
|
name = 'mojolicious'
|
|
136
136
|
extension = '.html.ep'
|
|
137
137
|
templatesPerComponent = true
|
|
138
|
+
// Template-string target with no component layer: `bf build` emits a static
|
|
139
|
+
// `barefoot-importmap.html` to `%= include` into the page <head> (#1644).
|
|
140
|
+
importMapInjection = 'html-snippet' as const
|
|
138
141
|
|
|
139
142
|
/**
|
|
140
143
|
* Identifier-path callees the Mojo runtime can render in template
|
|
@@ -1603,10 +1606,11 @@ function renderArrayMethod(
|
|
|
1603
1606
|
* plus the loop-hoist path in `renderLoop` — same emit shape across
|
|
1604
1607
|
* all three so a regression in any one path surfaces consistently.
|
|
1605
1608
|
*
|
|
1606
|
-
* The Perl helper accepts a hash-ref opts bag
|
|
1607
|
-
*
|
|
1608
|
-
*
|
|
1609
|
-
*
|
|
1609
|
+
* The Perl helper accepts a hash-ref opts bag whose `keys` entry is
|
|
1610
|
+
* an ordered list of per-key hashes (room for a future `nulls` knob
|
|
1611
|
+
* without arity churn), and returns a fresh ARRAY ref so downstream
|
|
1612
|
+
* composition (`@{bf->sort(...)}` in `join(...)`, etc.) stays
|
|
1613
|
+
* straightforward.
|
|
1610
1614
|
*/
|
|
1611
1615
|
/**
|
|
1612
1616
|
* Encode an `IRLoop.markerId` into a Perl-identifier-safe suffix
|
|
@@ -1626,11 +1630,18 @@ function perlIdentifierFromMarkerId(markerId: string): string {
|
|
|
1626
1630
|
}
|
|
1627
1631
|
|
|
1628
1632
|
function renderSortMethod(recv: string, c: SortComparator): string {
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1633
|
+
// One hash per comparison key, in priority order, under `keys`. A
|
|
1634
|
+
// simple comparator yields a one-element list; a `||`-chained
|
|
1635
|
+
// multi-key comparator yields one per operand. `bf->sort` walks them
|
|
1636
|
+
// in order, falling through to the next on a tie.
|
|
1637
|
+
const keyHashes = c.keys.map((k) => {
|
|
1638
|
+
const keyEntry =
|
|
1639
|
+
k.key.kind === 'self'
|
|
1640
|
+
? `key_kind => 'self'`
|
|
1641
|
+
: `key_kind => 'field', key => '${k.key.field}'`
|
|
1642
|
+
return `{ ${keyEntry}, compare_type => '${k.type}', direction => '${k.direction}' }`
|
|
1643
|
+
})
|
|
1644
|
+
return `bf->sort(${recv}, { keys => [${keyHashes.join(', ')}] })`
|
|
1634
1645
|
}
|
|
1635
1646
|
|
|
1636
1647
|
/**
|
package/src/test-render.ts
CHANGED
|
@@ -305,7 +305,7 @@ function buildChildRenderers(
|
|
|
305
305
|
const lines: string[] = []
|
|
306
306
|
lines.push(`# Register child component renderers`)
|
|
307
307
|
|
|
308
|
-
for (const [componentName] of childTemplates) {
|
|
308
|
+
for (const [componentName, { ir: childIR }] of childTemplates) {
|
|
309
309
|
const snakeName = toSnakeCase(componentName)
|
|
310
310
|
const childTemplatePath = resolve(tempDir, `${snakeName}.html.ep`)
|
|
311
311
|
|
|
@@ -326,6 +326,22 @@ function buildChildRenderers(
|
|
|
326
326
|
|
|
327
327
|
lines.push(` $bf->register_child_renderer('${snakeName}', sub {`)
|
|
328
328
|
lines.push(` my ($child_props) = @_;`)
|
|
329
|
+
// (#1652) A child that destructures a rest-spread bag
|
|
330
|
+
// (`function NativeSelect({ children, ...props })`) emits a
|
|
331
|
+
// template referencing `$<restPropsName>`
|
|
332
|
+
// (`<%== bf->spread_attrs($props) %>`). The parent's render_child
|
|
333
|
+
// call only forwards the props it explicitly passed (here just
|
|
334
|
+
// `children`), so the rest bag never reaches the child stash and
|
|
335
|
+
// Perl strict mode aborts with `Global symbol "$props" requires
|
|
336
|
+
// explicit package name`. Seed it with an empty hashref when the
|
|
337
|
+
// caller didn't supply one — mirroring the top-level harness path
|
|
338
|
+
// (`buildPerlProps`) and the production runtime's
|
|
339
|
+
// `_derive_stash_from_defaults` `isRestProps` branch, which plumbs
|
|
340
|
+
// the equivalent of Go's `Spread_0`/`Extras` Input field.
|
|
341
|
+
if (childIR.metadata.restPropsName) {
|
|
342
|
+
const rest = childIR.metadata.restPropsName
|
|
343
|
+
lines.push(` $child_props->{${rest}} = {} unless defined $child_props->{${rest}};`)
|
|
344
|
+
}
|
|
329
345
|
lines.push(` ${slotIdsPerl}`)
|
|
330
346
|
lines.push(` my $child_bf = BarefootJS->new($c, {});`)
|
|
331
347
|
lines.push(` $child_bf->_scope_id("test_$sid");`)
|