aboutsummaryrefslogtreecommitdiffstats
path: root/src/server/template/Base.ts
blob: e5128eebd0ba8f01f51b1e4da14ded49f09568ca (plain) (blame)
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import metadata from "../metadata.js"
import curl from "../utils/curl.js"
import setStingRoute from "../utils/setStingRoute.js"
import isDefined from "../utils/isDefined.js"
import { Attribute, doctype, html as html_1, head, meta, title as title_1, link, script, body, header, nav, a, main, footer } from "./html.js"
import { c, node } from "./vdom.js"
import type { URL } from "url"

export var pages: (ReturnType<typeof Base>)[] = []

type content<T extends node = node> = T | (() => T)

interface BasePageI<T extends node = node> {
	url?: string | URL | undefined,
	content: content<T>,
	date_mod?: Date | undefined,
	date_pub?: Date
}

export interface BaseI<T extends node = node> extends BasePageI<T> {
	title?: string,
	description?: string,
	keywords?: string[],
	isHighlight?: boolean
}

export function content<Type extends BasePageI>({ data }: Attribute & { data: Type }): node {
	return data.content instanceof Function ? data.content() : data.content
}

function BasePage<Type extends BasePageI>(data: Type): Type & {
	setupRoute(url?: URL): void;
	render(): Promise<string>;
	date_mod: Type["date_pub"];
	url: URL | undefined
} {
	return {
		...data,
		setupRoute(url?: URL): void {
			url ??= this.url
			if (isDefined(url)) {
				setStingRoute(url.pathname, "html", this.render.bind(this))
			} else {
				throw new Error();

			}
		},
		async render(): Promise<string> {
			console.profile()
			const a = doctype(c(content, { data: this }))
			console.profileEnd()
			return a
		},
		date_mod: data.date_mod ?? data.date_pub,
		url: isDefined(data.url) ? curl(data.url) : undefined,
	}
}

export default function Base<Type extends BaseI>(data: Type): ReturnType<typeof BasePage<Type>> {
	const arg = {
		...data,
		content() {
			var { title, description, keywords, url, isHighlight = true } = this
			if (!url) {
				isHighlight = false
			}
			return c(html_1, { lang: metadata.language },
				c(head, {},
					...(url ? [c(meta, { property: "og:locale", content: metadata.language })] : []),
					c(title_1, { property: "og:title" }, title || metadata.title),
					c(meta, { name: "theme-color", content: metadata.theme }),
					...(url ? [
						c(meta, { name: "description", property: "og:description", content: description || metadata.description }),
						...(Array.isArray(keywords) ? [c(meta, { name: "keywords", contents: keywords })] : []),
						c(meta, { property: "og:url", content: url }),
						c(link, { rel: "canonical", href: url }),
						c(meta, { property: "og:site_name", content: metadata.title }),
						c(meta, { name: "author", content: metadata.author.name }),
						c(meta, { property: "og:type", content: "website" }),
						c(meta, { name: "viewport", content: "width=device-width, initial-scale=1" }),
						c(meta, { name: "twitter:card", content: "summary" }),
						c(link, { rel: "alternate", href: metadata.feed.atom, type: "application/atom+xml", title: metadata.title }),
						c(meta, { property: "og:image", content: "/favicon.svg?format=png&width=1024" })
					] : []),
					c(link, { rel: "icon", href: "/favicon.ico", sizes: "any" }),
					c(link, { rel: "icon", href: "/favicon.svg", type: "image/svg+xml" }),
					c(link, { rel: "apple-touch-icon", href: "/favicon.svg?format:png&width=192" }),
					c(link, { rel: "manifest", href: "/app.webmanifest" }),
					c(link, { rel: "stylesheet", href: "/index.css" }),
					c(script, { type: "module", src: "/index.js" }),
					...(isHighlight ? [c(link, { rel: "stylesheet", href: "/syntax.css" })] : [])
				),
				c(body, {},
					c(header, {}, c(nav, {},
						c(a, { href: "#", id: "nav-toogle" }, "Sudomsg"),
						...Object.entries({
							Home: "/",
							Blog: "/blog",
							About: "/about",
							Git: "/cgit",
						}).map(([name, href]) => c(a, { href, class: "navlinks" }, name))
					)),
					c(main, {}, c(content, { data })),
					c(footer, {},
						"Subscribe: ",
						c(a, { href: metadata.feed.atom, type: "application/atom+xml" }, "Atom")
					)
				)
			)
		}
	}
	const base = BasePage(arg)
	if (isDefined(base.url)) {
		pages.push(base)
	}
	return base
}