Two packages compile components to Perl templates and share one runtime (BarefootJS): @barefootjs/mojolicious emits Mojolicious EP (.html.ep) and @barefootjs/xslate emits Text::Xslate Kolon (.tx), which runs under any PSGI/Plack app with no framework.
Scaffold a runnable starter:
npm create barefootjs@latest -- --adapter mojo # Mojolicious
npm create barefootjs@latest -- --adapter xslate # Plack/Starman + Text::Xslate#Install
npm install @barefootjs/mojolicious # or @barefootjs/xslate// vite.config.ts
import { defineConfig } from 'vite'
import { barefoot } from '@barefootjs/mojolicious/vite' // or '@barefootjs/xslate/vite'
export default defineConfig({
build: { outDir: 'dist/client' },
plugins: barefoot({ components: ['components'], templates: 'dist/templates' }),
})#Render from your server
Mojolicious. Load Mojolicious::Plugin::BarefootJS. It registers a bf helper (backed by BarefootJS::Backend::Mojo) that the generated templates call, and the templates render like any other .html.ep:
use Mojolicious::Lite -signatures;
plugin 'BarefootJS';
app->renderer->paths->[0] = app->home->child('dist/templates');
get '/' => sub ($c) {
$c->render(template => 'Counter', layout => 'default', initial => 0);
};
app->start;Print <%== $c->bf->scripts %> before </body> in the layout.
Text::Xslate (PSGI/Plack). The backend is a plain Text::Xslate instance, so any PSGI app can render:
use BarefootJS;
use BarefootJS::Backend::Xslate;
my $backend = BarefootJS::Backend::Xslate->new(path => ['dist/templates']);
my $app = sub {
my $bf = BarefootJS->new(undef, { backend => $backend });
$bf->_scope_id('Counter_' . int(rand(1e6)));
my $body = $backend->render_named('Counter', $bf, { initial => 0 });
my $html = "<!doctype html><body>$body" . $bf->scripts . '</body>';
return [200, ['Content-Type' => 'text/html; charset=utf-8'], [$html]];
};#Notes
- CPAN distributions:
BarefootJS(the runtime, core Perl only),BarefootJS-Backend-Xslate(addsText::Xslate), andMojolicious-Plugin-BarefootJS(addsMojolicious). The JS toolchain is needed at build time only. - EP escapes
<%= %>and prints raw with<%== %>; Kolon auto-escapes<: :>, and helpers that emit markup returnmark_rawvalues. - Dev auto-reload:
vite devrewritesdist/.dev/build-idafter every rebuild andBarefootJS::DevReloadpushes a reload over SSE. Mojolicious:plugin 'BarefootJS::DevReload'and%== bf_dev_snippetbefore</body>. Plack: mountBarefootJS::DevReload->to_app(dist_dir => 'dist')at an endpoint and printBarefootJS::DevReload->snippet('/_bf/reload')in the layout; use a prefork server (Starman) in dev. Both are no-ops in production. - Disable the template cache in development (
xslate_options => { cache => 0 }, orapp->renderer->cache->max_keys(0)in Mojolicious) so rebuilt templates render on the next request.
Examples: integrations/mojolicious and integrations/xslate.