@barefootjs/perl 0.9.2 → 0.9.4

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.
Files changed (2) hide show
  1. package/lib/BarefootJS.pm +59 -1
  2. package/package.json +1 -1
package/lib/BarefootJS.pm CHANGED
@@ -52,10 +52,11 @@ my %ATTR_DEFAULT = (
52
52
  # _scope_id — addressable scope id
53
53
  # _bf_parent / _bf_mount — slot identity when this scope is slot-attached
54
54
  # _props — props serialised into bf-p / the scope comment
55
+ # _data_key — keyed-loop-item key, emitted as data-key on the scope root
55
56
  for my $attr (qw(
56
57
  c config backend
57
58
  _scripts _script_seen _scope_id _is_child _bf_parent _bf_mount _props
58
- _child_renderers
59
+ _data_key _child_renderers
59
60
  )) {
60
61
  no strict 'refs';
61
62
  *{"BarefootJS::$attr"} = sub {
@@ -131,6 +132,20 @@ sub hydration_attrs ($self) {
131
132
  return join(' ', @parts);
132
133
  }
133
134
 
135
+ # Emits ` data-key="<key>"` for a keyed loop item, else ''. The client
136
+ # runtime uses data-key for list reconciliation; SSR must match the Hono
137
+ # reference, which stamps it on each loop item's scope root. The value is set
138
+ # on the child instance by the child renderer (`register_child_renderer` /
139
+ # `register_components_from_manifest`) from the JSX `key` prop — a reserved
140
+ # prop, never a real template variable.
141
+ sub data_key_attr ($self) {
142
+ my $k = $self->_data_key;
143
+ return '' unless defined $k;
144
+ $k =~ s/&/&amp;/g;
145
+ $k =~ s/"/&quot;/g;
146
+ return qq{ data-key="$k"};
147
+ }
148
+
134
149
  sub props_attr ($self) {
135
150
  my $props = $self->_props;
136
151
  return '' unless $props && %$props;
@@ -140,6 +155,45 @@ sub props_attr ($self) {
140
155
  return qq{ bf-p='$json'};
141
156
  }
142
157
 
158
+ # ---------------------------------------------------------------------------
159
+ # Context (SSR mirror of the client `provideContext` / `useContext`)
160
+ # ---------------------------------------------------------------------------
161
+ #
162
+ # A `<Ctx.Provider value>` seeds a value that descendant `useContext(Ctx)`
163
+ # consumers read during the same render. Dynamic scoping mirrors the client:
164
+ # the provider pushes the value before rendering its children and pops it
165
+ # after, and `use_context` reads the innermost active value (or the
166
+ # `createContext` default when none is active).
167
+ #
168
+ # The value stacks live in a package-level store rather than per-instance or
169
+ # on `$c->stash`: a parent template and the child templates it renders via
170
+ # `render_child` are separate bf instances that don't reliably share a
171
+ # controller (the Xslate backend runs with `c => undef`) nor a backend (the
172
+ # Mojo path lazily builds one per instance). SSR rendering is synchronous —
173
+ # nothing awaits between a provider's push and its matching pop — and the
174
+ # push/pop are perfectly balanced, so the per-name stack always unwinds to
175
+ # empty at the end of each provider subtree, keeping concurrent root renders
176
+ # isolated. provide/revoke return '' so they drop cleanly into an inline
177
+ # `<: … :>` (Kolon) or `% … ;` (EP) emit.
178
+
179
+ my %CONTEXT_STACKS;
180
+
181
+ sub provide_context ($self, $name, $value) {
182
+ push @{ $CONTEXT_STACKS{$name} //= [] }, $value;
183
+ return '';
184
+ }
185
+
186
+ sub revoke_context ($self, $name) {
187
+ pop @{ $CONTEXT_STACKS{$name} } if $CONTEXT_STACKS{$name} && @{ $CONTEXT_STACKS{$name} };
188
+ return '';
189
+ }
190
+
191
+ sub use_context ($self, $name, $default = undef) {
192
+ my $stack = $CONTEXT_STACKS{$name};
193
+ return $default unless $stack && @$stack;
194
+ return $stack->[-1];
195
+ }
196
+
143
197
  # ---------------------------------------------------------------------------
144
198
  # Comment Markers
145
199
  # ---------------------------------------------------------------------------
@@ -299,6 +353,10 @@ sub register_components_from_manifest ($self, $manifest, %opts) {
299
353
  # closure adds no edge to the per-request reference cycle.
300
354
  my $child_bf = BarefootJS->new($parent->c, { backend => $parent->backend });
301
355
  my $slot_id = delete $props->{_bf_slot};
356
+ # JSX `key` (a reserved prop) → data-key on the child's scope root
357
+ # for keyed-loop reconciliation (see `data_key_attr`).
358
+ my $data_key = delete $props->{key};
359
+ $child_bf->_data_key($data_key) if defined $data_key;
302
360
  $child_bf->_scope_id(
303
361
  $slot_id ? $parent_scope . '_' . $slot_id
304
362
  : $template_name . '_' . substr(rand() =~ s/^0\.//r, 0, 6)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/perl",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "BarefootJS engine-agnostic Perl runtime (BarefootJS.pm) — pluggable rendering backend; the shared basis for Mojolicious and other Perl framework integrations",
5
5
  "type": "module",
6
6
  "files": [