BarefootJS
view release on metacpan or search on metacpan
lib/BarefootJS.pm view on Meta::CPAN
# See spec/compiler.md "Slot identity".
sub hydration_attrs ($self) {
my @parts;
my $host = $self->_bf_parent;
my $mount = $self->_bf_mount;
if (defined $host && length $host) {
my $h = $host =~ s/"/"/gr;
push @parts, qq{bf-h="$h"};
}
if (defined $mount && length $mount) {
my $m = $mount =~ s/"/"/gr;
push @parts, qq{bf-m="$m"};
}
unless ($self->_is_child) {
push @parts, q{bf-r=""};
}
return join(' ', @parts);
}
# Emits ` data-key="<key>"` for a keyed loop item, else ''. The client
# runtime uses data-key for list reconciliation; SSR must match the Hono
# reference, which stamps it on each loop item's scope root. The value is set
# on the child instance by the child renderer (`register_child_renderer` /
# `register_components_from_manifest`) from the JSX `key` prop â a reserved
# prop, never a real template variable.
sub data_key_attr ($self) {
my $k = $self->_data_key;
return '' unless defined $k;
$k =~ s/&/&/g;
$k =~ s/"/"/g;
return qq{ data-key="$k"};
}
sub props_attr ($self) {
my $props = $self->_props;
return '' unless $props && %$props;
# encode_json returns a character string (not bytes) for safe embedding
# in templates (the Mojo backend uses Mojo::JSON::to_json).
# The JSON must then be attribute-escaped: a raw `'` inside a string
# value (e.g. a blog paragraph) terminates the single-quoted attribute
# and truncates the hydration payload. The browser entity-decodes the
# attribute value, so the client's JSON.parse sees the original text.
my $json = _html_escape($self->backend->encode_json($props));
return qq{ bf-p='$json'};
}
# ---------------------------------------------------------------------------
# Context (SSR mirror of the client `provideContext` / `useContext`)
# ---------------------------------------------------------------------------
#
# A `<Ctx.Provider value>` seeds a value that descendant `useContext(Ctx)`
# consumers read during the same render. Dynamic scoping mirrors the client:
# the provider pushes the value before rendering its children and pops it
# after, and `use_context` reads the innermost active value (or the
# `createContext` default when none is active).
#
# The value stacks live in a package-level store rather than per-instance or
# on `$c->stash`: a parent template and the child templates it renders via
# `render_child` are separate bf instances that don't reliably share a
# controller (the Xslate backend runs with `c => undef`) nor a backend (the
# Mojo path lazily builds one per instance). SSR rendering is synchronous â
# nothing awaits between a provider's push and its matching pop â and the
# push/pop are perfectly balanced, so the per-name stack always unwinds to
# empty at the end of each provider subtree, keeping concurrent root renders
# isolated. provide/revoke return '' so they drop cleanly into an inline
# `<: ⦠:>` (Kolon) or `% ⦠;` (EP) emit.
my %CONTEXT_STACKS;
sub provide_context ($self, $name, $value) {
push @{ $CONTEXT_STACKS{$name} //= [] }, $value;
return '';
}
sub revoke_context ($self, $name) {
pop @{ $CONTEXT_STACKS{$name} } if $CONTEXT_STACKS{$name} && @{ $CONTEXT_STACKS{$name} };
return '';
}
sub use_context ($self, $name, $default = undef) {
my $stack = $CONTEXT_STACKS{$name};
return $default unless $stack && @$stack;
return $stack->[-1];
}
# ---------------------------------------------------------------------------
# Comment Markers
# ---------------------------------------------------------------------------
sub comment ($self, $text) {
return "<!--bf-$text-->";
}
# ---------------------------------------------------------------------------
# JS-equivalent value stringification
# ---------------------------------------------------------------------------
# Map a Perl boolean-shaped value to the JS `String(bool)` form.
# Used by the Mojo adapter when emitting reactive attribute bindings
# whose JS source `isBooleanResultExpr` classified as boolean â
# a comparison (`count() > 0`), a logical negation (`!ok()`), or a
# literal `true` / `false`. Perl's auto-stringification of those
# expressions yields `''` / `1`; Hono and Go emit `'false'` / `'true'`.
# Centralising the bool â string mapping here keeps the contract
# testable and the template-emit syntax tidy
# (`<%= bf->bool_str(...) %>` vs an inline ternary).
#
# Contract is boolean-only: callers must have classified the
# expression as boolean-result before routing through this helper.
# Non-boolean values reaching here will be Perl-truthy-coerced to
# 'true' / 'false', which is generally wrong â non-boolean attribute
# bindings stay on the plain `<%= expr %>` emit path and never reach
# this function.
sub bool_str ($self, $value) {
return $value ? 'true' : 'false';
}
sub text_start ($self, $slot_id) {
return "<!--bf:$slot_id-->";
}
( run in 1.791 second using v1.01-cache-2.11-cpan-9581c071862 )