BarefootJS
view release on metacpan or search on metacpan
lib/BarefootJS.pm view on Meta::CPAN
# terminates it early, and &/</>/"/' are not special there. The key's exact
# text doesn't need to round-trip (the client's mapArrayAnchored matches
# items positionally and by its own JS-computed key, never by re-parsing
# the anchor Comment.nodeValue), so replacing every "-" with the
# visually-similar U+2010 is sufficient and needs no decoding.
sub escape_comment_key ($self, $value) {
my $s = $self->string($value);
$s =~ s/-/â/g;
return $s;
}
# ---------------------------------------------------------------------------
# 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-->";
}
sub text_end ($self) {
return "<!--/-->";
}
# See spec/compiler.md "Slot identity" for the comment-scope wire format.
sub scope_comment ($self) {
my $scope_id = $self->_scope_id // '';
my $host_segment = '';
my $host = $self->_bf_parent;
my $mount = $self->_bf_mount;
if (defined $host && length $host) {
$host_segment = "|h=$host|m=" . ($mount // '');
}
my $props_json = '';
if ($self->_props && %{$self->_props}) {
$props_json = '|' . $self->backend->encode_json($self->_props);
}
return "<!--bf-scope:$scope_id$host_segment$props_json-->";
}
# Paired end marker for scope_comment above. Bounds the scope's sibling
# range so client-side queries from a fragment-rooted scope don't leak
# onto later siblings owned by the parent (#2289). No `|h=`/`|m=`/props
# segments â the client only needs the scope id to find the matching end.
sub scope_comment_end ($self) {
my $scope_id = $self->_scope_id // '';
return "<!--bf-/scope:$scope_id-->";
}
# ---------------------------------------------------------------------------
# Script Registration
# ---------------------------------------------------------------------------
sub register_script ($self, $path) {
return if $self->_script_seen->{$path};
$self->_script_seen->{$path} = 1;
push @{$self->_scripts}, $path;
}
# Register a `<link rel="modulepreload">` hint â mirrors register_script
# exactly: same dedup-by-path hash, same insertion-order arrayref, same
# lifetime/reset (child-propagation) semantics, same no-output-string
# return so a template's `<: $bf->register_preload(...) :>` /
# `% $bf->register_preload(...);` emits no bytes where it sits. The
# `<link>` tag itself is only ever produced by `scripts` below, never
# here â a preload registration must never inject a node into a
# component's own template output.
sub register_preload ($self, $path) {
return if $self->_preload_seen->{$path};
$self->_preload_seen->{$path} = 1;
push @{$self->_preloads}, $path;
}
# ---------------------------------------------------------------------------
# Child Component Rendering
# ---------------------------------------------------------------------------
# (`_child_renderers` accessor is generated by the minimal accessor base above.)
# Register a renderer for `render_child($name, ...)`. The renderer is
# invoked as `$renderer->($props_hashref, $invoking_bf)` â unpack `@_`
# (`my ($props, $caller) = @_;`) instead of declaring a one-argument
# subroutine signature, which would enforce arity and die on the second
# argument.
sub register_child_renderer ($self, $name, $renderer) {
$self->_child_renderers->{$name} = $renderer;
}
sub render_child ($self, $name, @args) {
my $renderer = $self->_child_renderers->{$name};
die "No renderer registered for child component '$name'" unless $renderer;
# Accept both the Mojo list form â `bf->render_child($name, k => v, ...)`
# â and the single-hashref form â `$bf.render_child($name, { k => v })`.
# Template languages whose method calls can't splat a hash into positional
# args (Text::Xslate Kolon, Template Toolkit) pass one hashref instead.
my %props = (@args == 1 && ref $args[0] eq 'HASH') ? %{ $args[0] } : @args;
# JSX children AND any other named JSX-valued slot (`header={<strong/>}`,
# #2168 jsx-element-prop) come in via the engine's children-capture
# mechanism (Mojo's `begin %>...<% end`, which produces a CODE ref
# returning a Mojo::ByteStream). Materialize every prop value through
# the backend before handing the props to the child renderer, so the
# child template sees each slot as already-rendered HTML rather than a
# bare CODE ref â `materialize` is a no-op for a value that isn't a
( run in 1.440 second using v1.01-cache-2.11-cpan-b16cb0d3907 )