Skip to content

Commit

Permalink
add svelte html pre-rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoMcA authored and akatsoulas committed Oct 14, 2022
1 parent e8d178c commit b05b8eb
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 6 deletions.
9 changes: 9 additions & 0 deletions docs/svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,12 @@ div {
This is because neither `svelte-preprocess` nor the Webpack `sass-loader` are able to chunk split `@import`s and `@use`s,
or even de-duplicate their use across Svelte components (due to the scoped nature of the CSS within).
Not doing this will lead to unnecessarily duplicated code.

## Pre-rendering

To pre-render Svelte components a two-step process is required.

First the components must pass through the Svelte compiler,
with the appropriate flags enabled to compile components for server-side rendering (SSR).
Then those compiled components must be rendered into static HTML.
We do both these steps in Webpack using the `webpack.pre-render.js` config file.
8 changes: 7 additions & 1 deletion kitsune/landings/jinja2/landings/contribute.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
{% extends 'base.html' %}
{% set scripts = ("contribute",) %}

{% block head_title %}
{% include "pre-render" + path + ".head.html" %}
{% endblock %}

{% block contentwrap %}
<div class="mzp-l-content" id="main-content"></div>
<div class="mzp-l-content" id="main-content">
{% include "pre-render" + path + ".html" %}
</div>
{% endblock %}
11 changes: 10 additions & 1 deletion kitsune/landings/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from django.http import Http404
from django.shortcuts import render
from jinja2 import TemplateNotFound

from kitsune.products.models import Product
from kitsune.sumo.decorators import ssl_required
Expand Down Expand Up @@ -44,4 +46,11 @@ def integrity_check(request):


def contribute(request):
return render(request, "landings/contribute.html")
try:
return render(
request,
"landings/contribute.html",
{"path": request.path.removesuffix("/").lower()},
)
except TemplateNotFound:
raise Http404
5 changes: 4 additions & 1 deletion kitsune/sumo/jinja2/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
<head>
{% block head_top %}{% endblock %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>{% block title %}{% if title %}{{ title }} | {% endif %}{{ pgettext('site_title', 'Mozilla Support') }}{% endblock %}</title>

{% block head_title %}
<title>{% block title %}{% if title %}{{ title }} | {% endif %}{{ pgettext('site_title', 'Mozilla Support') }}{% endblock %}</title>
{% endblock %}

{{ favicon() }}

Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
"start": "concurrently --raw --kill-others \"npm run webpack:watch\" \"npm run browser-sync\"",
"lint:webpack": "npx eslint --no-eslintrc -c webpack/eslintrc.js kitsune",
"webpack:build": "npx webpack build --config webpack.dev.js",
"webpack:build:pre-render": "npx webpack build --config webpack.pre-render.js",
"webpack:build:prod": "npx webpack build --config webpack.prod.js",
"webpack:watch": "npx webpack watch --config webpack.dev.js",
"webpack:watch": "npx concurrently npm:webpack:watch:*",
"webpack:watch:dev": "npx webpack watch --config webpack.dev.js",
"webpack:watch:pre-render": "npx webpack watch --config webpack.pre-render.js",
"webpack:test": "npx webpack build --config webpack.test.js && npx mocha --require ./webpack/mocha-require dist/tests.js"
},
"license": "MPL-2.0",
Expand Down
3 changes: 2 additions & 1 deletion svelte/contribute/Contribute.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Router, Route } from "svelte-navigator";
import Picker from "./Picker";
export let url = "";
export let locale = "";
</script>

Expand All @@ -12,7 +13,7 @@
<div class="sumo-page-section--inner">
<h1>{gettext("Contribute Page Scaffolding")}</h1>

<Router basepath="/{locale}/contribute">
<Router basepath="/{locale}/contribute" {url}>
<Route path="forum">
<p>{gettext("This is the forum contribution area")}</p>
<Picker />
Expand Down
3 changes: 2 additions & 1 deletion svelte/contribute/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import Contribute from "./Contribute";

new Contribute({
target: document.querySelector("#main-content"),
hydrate: true,
props: { locale: window.location.pathname.split("/")[1] },
});
});
3 changes: 3 additions & 0 deletions webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ module.exports = {
options: {
emitCss: true,
preprocess: sveltePreprocess(),
compilerOptions: {
hydratable: true,
}
},
},
},
Expand Down
61 changes: 61 additions & 0 deletions webpack.pre-render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const path = require("path");
const sveltePreprocess = require("svelte-preprocess");
const SveltePreRenderPlugin = require("./webpack/svelte-pre-render-plugin");

module.exports = {
entry: {
contribute: "./svelte/contribute/Contribute",
},
plugins: [
new SveltePreRenderPlugin({
"contribute.js": [
"/contribute",
"/contribute/forum",
"/contribute/kb",
"/contribute/social",
]
})
],
mode: "production",
resolve: {
alias: {
svelte: path.resolve("node_modules", "svelte"),
},
extensions: [".mjs", ".js", ".svelte"],
mainFields: ["svelte", "browser", "module", "main"],
},
module: {
rules: [
{
test: /\.svelte$/,
use: {
loader: "svelte-loader",
options: {
emitCss: false,
preprocess: sveltePreprocess(),
compilerOptions: {
generate: "ssr",
hydratable: true,
},
},
},
},
{
// required to prevent errors from Svelte on Webpack 5+, omit on Webpack 4
test: /node_modules\/svelte\/.*\.mjs$/,
resolve: {
fullySpecified: false,
},
},
],
},
devtool: "cheap-module-source-map",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist/pre-render"),
library: {
type: "commonjs",
},
},
target: "node",
};
59 changes: 59 additions & 0 deletions webpack/svelte-pre-render-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const fs = require("fs");

function rerequire(path) {
delete require.cache[path];
return require(path);
}

class SveltePreRenderPlugin {
constructor(options = {}) {
this.options = options;
}

apply(compiler) {
const locales = fs.readdirSync("jsi18n/jsi18n");

compiler.hooks.assetEmitted.tap(
"svelte-pre-render-plugin",
(file, { outputPath, targetPath }) => {
const routes = this.options[file];
if (routes) {
const Component = rerequire(targetPath).default;

for (const locale of locales) {
const jsi18n = rerequire(`../jsi18n/jsi18n/${locale}/djangojs.js`);
Object.assign(global, jsi18n);

for (const route of routes) {
const { head = "", html } = Component.render({
url: `/${locale}${route}`,
locale,
});

fs.mkdirSync(
`${outputPath}/${locale}${route
.split("/")
.slice(0, -1)
.join("/")}`,
{
recursive: true,
}
);

fs.writeFileSync(
`${outputPath}/${locale}${route}.head.html`,
head
);
fs.writeFileSync(
`${outputPath}/${locale}${route}.html`,
html
);
}
}
}
}
);
}
}

module.exports = SveltePreRenderPlugin;

0 comments on commit b05b8eb

Please sign in to comment.