Repository navigation
Expand file tree
/
Copy pathhandlebars-pages.js
More file actions
80 lines (76 loc) · 2.85 KB
/
Copy pathhandlebars-pages.js
File metadata and controls
80 lines (76 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Adds what vite-plugin-handlebars lacks: YAML frontmatter and layouts. Before that
// plugin compiles a page, this one parses the frontmatter, keeps it as the page's
// context and wraps the body in a partial block for the layout it names, so
// `{{> @partial-block }}` in the layout receives the page. The layout directory is
// registered as a partial directory alongside the others.
import { basename } from "node:path";
import Handlebars from "handlebars";
import matter from "gray-matter";
import handlebars from "vite-plugin-handlebars";
import { normalizePath } from "vite";
export default function handlebarsPages({
data = {},
layoutDirectory,
partialDirectory = [],
defaultLayout = "default",
helpers,
compileOptions,
}) {
const frontmatter = new Map();
// Frontmatter strings may use Handlebars expressions, such as a meta_description that
// quotes {{formatNumber stats.icons.lucide}}; they render with the global data. The layout
// escapes them where it outputs them.
const strings = Handlebars.create();
strings.registerHelper(helpers ?? {});
const renderFrontmatter = (values) =>
Object.fromEntries(
Object.entries(values).map(([key, value]) => [
key,
typeof value === "string" && value.includes("{{")
? strings.compile(value, { noEscape: true })({ ...data, ...values })
: value,
])
);
return [
{
name: "handlebars-pages",
configureServer(server) {
// Layouts and partials live outside the Vite root, so watch them and reload when one
// changes, appears or disappears. vite-plugin-handlebars' own reload is switched off
// below: it compares un-normalised paths and misses on Windows, and would double up here.
const directories = [layoutDirectory, ...partialDirectory].map(
normalizePath
);
server.watcher.add(directories);
const reload = (file) => {
const changed = normalizePath(file);
if (directories.some((dir) => changed.startsWith(dir))) {
server.ws.send({ type: "full-reload" });
}
};
server.watcher.on("change", reload);
server.watcher.on("add", reload);
server.watcher.on("unlink", reload);
},
transformIndexHtml: {
order: "pre",
handler(html, ctx) {
const page = matter(html);
frontmatter.set(
normalizePath(ctx.path),
renderFrontmatter(page.data)
);
const layout = basename(page.data.layout ?? defaultLayout, ".hbs");
return `{{#> ${layout}}}${page.content}{{/${layout}}}`;
},
},
},
handlebars({
partialDirectory: [layoutDirectory, ...partialDirectory],
reloadOnPartialChange: false,
helpers,
compileOptions,
context: (pagePath) => ({ ...data, ...frontmatter.get(pagePath) }),
}),
];
}