BarefootJS
view release on metacpan or search on metacpan
lib/BarefootJS.pm view on Meta::CPAN
}
# Render the child template with $child_bf bound as the active
# instance for the nested render. The backend owns the
# engine-specific binding + restore (stash juggle for Mojo).
my $html = $parent->backend->render_named(
$template_name, $child_bf, { %$props, %extra },
);
chomp $html;
return $html;
});
}
}
# Derive template-stash kvs from a manifest entry's `ssrDefaults`
# section. Each entry shape:
# { value => <static-fallback>, propName => <prop>, isRestProps => bool }
# For `isRestProps`, the rest bag passes through unchanged (or the
# static `{}` if the caller didn't supply one). For ordinary entries
# the caller's `$props->{propName}` wins when defined, otherwise the
# static `value` does. `propName`-less entries (signal / memo locals)
# always use the static value â the caller cannot override them.
sub _derive_stash_from_defaults ($defaults, $props) {
my %extra;
for my $name (keys %$defaults) {
my $d = $defaults->{$name};
if (ref($d) ne 'HASH') {
$extra{$name} = $d;
next;
}
if ($d->{isRestProps}) {
$extra{$name} = exists $props->{$name} ? $props->{$name} : $d->{value};
next;
}
my $prop_name = $d->{propName};
if (defined $prop_name && exists $props->{$prop_name} && defined $props->{$prop_name}) {
$extra{$name} = $props->{$prop_name};
} else {
$extra{$name} = $d->{value};
}
}
return %extra;
}
# ---------------------------------------------------------------------------
# Script Output
# ---------------------------------------------------------------------------
sub scripts ($self) {
my @tags;
for my $path (@{$self->_scripts}) {
push @tags, qq{<script type="module" src="$path"></script>};
}
return join("\n", @tags);
}
# ---------------------------------------------------------------------------
# Streaming SSR (Out-of-Order)
# ---------------------------------------------------------------------------
sub streaming_bootstrap ($self) {
return q{<script>(function(){function s(id){var a=document.querySelector('[bf-async="'+id+'"]');var t=document.querySelector('template[bf-async-resolve="'+id+'"]');if(!a||!t)return;a.replaceChildren(t.content.cloneNode(true));a.removeAttribute('b...
}
sub async_boundary ($self, $id, $fallback_html) {
# The fallback comes in via Mojo `begin %>...<% end` capture (see
# MojoAdapter::renderAsync), which produces a CODE ref returning a
# Mojo::ByteStream. Materialize it through the backend so the rendered
# HTML embeds in the placeholder rather than the CODE ref's
# stringification.
$fallback_html = $self->backend->materialize($fallback_html);
return qq{<div bf-async="$id">$fallback_html</div>};
}
sub async_resolve ($self, $id, $content_html) {
return qq{<template bf-async-resolve="$id">$content_html</template><script>__bf_swap("$id")</script>};
}
# ---------------------------------------------------------------------------
# JS-compat callees (#1189) â invoked from generated Mojo templates as
# <%= bf->json($val) %>, <%= bf->floor($val) %>, etc. The MojoAdapter's
# `templatePrimitives` registry emits these helper calls in place of the
# corresponding JS callees (`JSON.stringify`, `Math.floor`, â¦) so the SSR
# template can render value-equivalent output without a JS engine.
#
# Failure policy mirrors the Go adapter (#1188): user-data marshalling
# (json) bubbles errors so Mojolicious aborts loudly on cycles /
# unsupported values rather than silently producing an empty payload.
# Numeric coercion follows JS semantics (NaN propagates as the special
# string 'NaN'; non-numeric input returns 'NaN' rather than 0). Strings
# always coerce to a string representation.
# ---------------------------------------------------------------------------
sub json ($self, $value) {
# Mojo::JSON::to_json returns a character string (not bytes), suitable
# for embedding in HTML output via Mojo::ByteStream / `<%==`.
#
# Documented divergence from JS: JS distinguishes `null` (renders as
# "null") from `undefined` (`JSON.stringify(undefined)` returns the
# JS value `undefined`, not a string). Perl has no such distinction
# â both map to `undef`. We choose the `null` rendering for SSR
# ergonomics: an unset prop becomes the string "null" rather than
# the literal text "undefined" or an empty attribute. Matches the
# `null` case of JS exactly; diverges from the `undefined` case.
return $self->backend->encode_json($value);
}
sub string ($self, $value) {
# JS `String(v)` mirror. `undef` renders as the empty string here so
# an unset prop doesn't surface as a literal "undefined" / "null"
# in user-facing HTML â same divergence the Go adapter documents
# for `bf_string`.
return defined $value ? "$value" : '';
}
sub number ($self, $value) {
# JS `Number(v)` mirror. Numeric coerces via Perl's implicit
# numeric context; non-numeric / undef yield real numeric NaN
# (`'nan' + 0`) so downstream arithmetic propagates correctly
# (`Math.floor(NaN) === NaN`). Returning the literal string
# "NaN" would conflate the user-passing-the-string-"NaN" case
( run in 1.839 second using v1.01-cache-2.11-cpan-140bd7fdf52 )