@elo7/fastify-svelte
v3.0.0
Published
Fastify plugin for using Svelte as a view engine (server-side rendering)
Downloads
1
Readme
fastify-svelte
Fastify plugin for using Svelte as a view engine (server-side rendering)
Table of Contents
Installation
To install this plugin, run the command:
# npm
npm install --save @elo7/fastify-svelte
# yarn
yarn add @elo7/fastify-svelte
Usage
Using your fastify
instance, register the plugin:
// src/server.js
import fastify from 'fastify';
import sveltePlugin from 'fastify-svelte';
const app = fastify();
app.register(sveltePlugin);
The plugin accepts some options, which are detailed in the "Constructor's API" section below.
Consider that you have, the following file structure:
|-> rollup.config.js
|-> src/
| |-> server.js
| |-> views/
| | |-> Home/
| | | |-> template.svelte
| | |-> template/
| | | |-> template.js
| |-> controller/
| | |-> Home.js
In your controller, for returning an visual page as a response do:
// src/controllers/Home.js
app.get('/', (req, reply) => {
reply.view('Home', { name: 'John Doe' }, { themeColor: '#fff' });
});
This will send the server side rendered home page, constructed using the second argument as props for the page, and the third argument is passed to the generic template as is.
The plugin expects that you have compiled your Svelte templates beforehand to a ES Module format. You can do this by using a bundler such as Rollup or Webpack in your project. For an example, your rollup.config.js
could look like this:
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import glob from 'glob';
const templates = glob.sync('src/**/template.svelte');
const dev = NODE_ENV === 'development';
const serverSideConfig = (template) => ({
input: template,
output: {
file: template.replace('svelte', 'js'),
format: 'esm',
},
plugins: [
svelte({
css: (css) => {
css.write(template.replace('src', 'static/css').replace('svelte', 'css'), false);
},
dev,
generate: 'ssr',
}),
resolve(),
commonjs(),
],
});
const clientSideConfig = (template) => ({
input: template,
output: {
file: template.replace('src', 'static/js').replace('svelte', 'js'),
format: 'iife',
name: 'Page',
},
plugins: [
svelte({
css: false,
dev,
hydratable: true,
}),
resolve(),
commonjs(),
],
});
export default [
...templates.map(serverSideConfig),
...templates.map(clientSideConfig),
];
Your generic template.js
should look like this, this will be used as the
skeleton for your page, adding meta tags and providing anchors for your
svelte page to be mounted, this template receives an object that contains
both the css, the script and the html (named as content here) generated by
the svelte page compilation, the props used to hydrate the component, once
the page is loaded by the user and the extraData passed throgh the controller
// src/views/template/template.js
export default ({ css, script, content, props, extraData }) => `<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' href='${css}'>
<meta name="theme-color" content="${extraData.themeColor}">
</head>
<body>
<div id='content'>
${content}
</div>
<script src='${script}'></script>
<script>
new Page({
hydrate: true,
props: ${props},
target: document.getElementById('content'),
});
</script>
</body>
</html>`;
View method's API
This plugin will make available the reply.view
method, which receives three
arguments:
Path (first argument)
Required: true
This is the path of the directory where the file template.svelte
is,
the plugin's option svelteTemplatesPath
will be appended to argument
path, as well as the return of the platform
option. For an example look
at the assetUrlForConstructor
explanation.
Props (second argument)
Required: false
This is the props object that will be used to render the component passed through the first argument.
Extra data (third argument)
Required: false
This object will be passed to your generic template.js
as is.
Constructor's API
This plugin can receive three options, all are optional:
Platform
Default: () => ''
The platform
argument is used to be appended with the
path of an specified page. In the example below, the
plugin will look for the templates under
src/views/<path>/mobile
or src/views/<path>/desktop
:
const isMobile = (reply) => // implement your logic here.
app.register(sveltePlugin, {
platform: (reply) => isMobile(reply) ? 'mobile' : 'desktop',
});
SvelteTemplatesPath
Default: <rootdir>/src/views
The svelteTemplatesPath
argument is used to specify the
path where templates are found. In the example below, the
plugin will look for the templates under
<rootdir>/src/pageViews
:
app.register(sveltePlugin, {
svelteTemplatesPath: `{__dirname}/pageViews`,
});
Note that the code present in the example assumes that
you are calling the plugin's constructor under <rootdir>/src
.
AssetUrlForConstructor
Default:
() => (page, type) => {
return `/static/${type}/views/${page}/template.${type}`;
};
The assetUrlForConstructor
argument is used to specify
how the path of a page is translated in its assets,
the javascript file and the css one. It receives
the fastify instance as its only argument, and it
must return a function, which receives two arguments
and returns a path to an asset.
The page
argument will be the path given to the reply.view
first argument with the return of the platform
function
appended, for example, if you have a code that look like this:
// src/server.js
app.register(sveltePlugin, {
platform: () => 'mobile',
});
// src/controllers/Home.js
app.get('/', (req, reply) => {
return reply.view('pages/Home');
};
The page
argument value will be pages/Home/mobile
. And the
type
argument value will be either js
or css
. Below, we have an
example where the asset's path is just static
folder and the function logs to
the console when a template is found.
app.register(sveltePlugin, {
assetUrlForConstructor: (app) => {
app.log.info('Template found');
return (page, type) => `/static/${page}.${type}`
}
});
Plugins
This fastify's plugin accepts its own plugins, so you can extend its functionality. The plugins are passed as itens in an array.
import sveltePartialHydration from
'@elo7/fastify-svelte-parital-hydration-plugin`;
app.register(sveltePlugin, {
plugins: [sveltePartialHydration],
});
Plugins
This fastify's plugin accepts its own plugins, so you can extend its
functionality. Each plugins specifies in which stage of the
fastify-svelte process and the key that the value returned by the plugin
will occupy in the object passed to the template.js
. For an
example on how to build a plugin, check Partial Hydration Plugin.