File Coverage

blib/lib/BarefootJS/Backend/Xslate.pm
Criterion Covered Total %
statement 59 63 93.6
branch 4 6 66.6
condition 2 5 40.0
subroutine 13 14 92.8
pod 5 6 83.3
total 83 94 88.3


line stmt bran cond sub pod time code
1             package BarefootJS::Backend::Xslate;
2             our $VERSION = "0.15.2";
3 1     1   190504 use strict;
  1         2  
  1         28  
4 1     1   4 use warnings;
  1         2  
  1         39  
5 1     1   3 use utf8;
  1         1  
  1         28  
6 1     1   27 use feature 'signatures';
  1         1  
  1         94  
7 1     1   3 no warnings 'experimental::signatures';
  1         2  
  1         31  
8              
9 1     1   526 use Text::Xslate ();
  1         10156  
  1         28  
10 1     1   660 use JSON::PP ();
  1         13098  
  1         355  
11              
12             # ---------------------------------------------------------------------------
13             # Text::Xslate (Kolon) rendering backend for the BarefootJS runtime.
14             # ---------------------------------------------------------------------------
15             #
16             # The engine-agnostic runtime logic — the JS-compat value helpers, array/string
17             # methods, hydration markers, child rendering — lives in BarefootJS
18             # (@barefootjs/perl). This backend supplies the four engine-specific operations
19             # the runtime delegates to, targeting Text::Xslate's Kolon syntax:
20             #
21             # encode_json($data) -> JSON string (injectable encoder)
22             # mark_raw($str) -> Text::Xslate raw value (no re-escaping)
23             # materialize($value) -> resolve a captured-children value to a string
24             # render_named($name, $bf, \%v) -> render `.tx` with `bf` + vars bound
25             #
26             # Pair it with the @barefootjs/xslate compile-time adapter, which emits Kolon
27             # `.tx` templates that call the runtime as a `$bf` object: `<: $bf.scope_attr()
28             # :>`, `<: $bf.json($x) :>`, `<: $bf.spread_attrs($bag) :>`. Kolon auto-escapes
29             # `<: ... :>` interpolations (`type => 'html'`); helpers that emit markup return
30             # `mark_raw` values (or the template adds `| mark_raw`), mirroring Mojo EP's
31             # `<%==` vs `<%=` distinction.
32             #
33             # Unlike the Mojo backend, this has no dependency on a web framework: a plain
34             # Text::Xslate instance renders templates from a path, so it runs under any
35             # PSGI / Plack app (or none at all).
36              
37 1     1 1 141540 sub new ($class, %args) {
  1         2  
  1         3  
  1         2  
38 1   33     6 my $json_encoder = $args{json_encoder} // do {
39             # Default pure-Perl encoder. `canonical` keeps key order deterministic
40             # (matching the runtime's sorted-key SSR policy); `allow_nonref` lets
41             # scalars / undef encode as `"x"` / `null`. Swap via `json_encoder`
42             # for a faster XS implementation.
43 1         8 my $j = JSON::PP->new->canonical->allow_nonref;
44 1     1   60 sub ($data) { return $j->encode($data) };
  1         8  
  1         3  
  1         3  
  1         2  
45             };
46              
47             # Accept a pre-built Text::Xslate instance, or build one from `path`
48             # (a dir of `.tx` templates) plus any extra `xslate_options`. The adapter
49             # calls every runtime helper as a `$bf` method (`$bf.filter`, `$bf.lc`, …)
50             # or a Kolon builtin (`.join`, `.size`), so no custom `function` map is
51             # needed here — a plain Kolon, html-escaping instance suffices.
52 1         3 my $xslate = $args{xslate};
53 1 50       3 unless ($xslate) {
54             $xslate = Text::Xslate->new(
55             syntax => 'Kolon',
56             type => 'html',
57             ($args{path} ? (path => $args{path}) : ()),
58 1 50 50     4 %{ $args{xslate_options} // {} },
  1         18  
59             );
60             }
61              
62 1         240 return bless { xslate => $xslate, json_encoder => $json_encoder }, $class;
63             }
64              
65 0     0 0 0 sub xslate ($self) { return $self->{xslate} }
  0         0  
  0         0  
  0         0  
66              
67 1     1 1 539 sub encode_json ($self, $data) {
  1         3  
  1         2  
  1         31  
68 1         7 return $self->{json_encoder}->($data);
69             }
70              
71             # Mark a string as already-safe so Kolon emits it verbatim (no auto-escape).
72 1     1 1 104756 sub mark_raw ($self, $str) {
  1         4  
  1         3  
  1         3  
73 1         25 return Text::Xslate::mark_raw($str);
74             }
75              
76             # JSX children captured by the adapter (a Kolon macro call yields a rendered
77             # string; some paths may pass a CODE ref) resolve to a string here.
78 2     2 1 10425 sub materialize ($self, $value) {
  2         5  
  2         5  
  2         3  
79 2 100       17 return ref($value) eq 'CODE' ? $value->() : $value;
80             }
81              
82             # Render `.tx` with `$child_bf` bound as the `bf` object for the nested
83             # render, plus the supplied template vars. No stash juggling is needed: Kolon
84             # resolves `$bf` from the per-render vars, so each child render gets its own
85             # instance directly.
86 1     1 1 41 sub render_named ($self, $template_name, $child_bf, $vars) {
  1         2  
  1         2  
  1         2  
  1         1  
  1         1  
87 1         14 return $self->{xslate}->render("$template_name.tx", { %$vars, bf => $child_bf });
88             }
89              
90             1;
91             __END__