1/** the graph that builds and serves the site, for steps that run when a request asks, with `graph.lazy` */
2export let graph: incremental.Graph;
3
4/** set before the backend loads */
5export function setGraph(next: incremental.Graph) {
6 graph = next;
7}
8
9export type ScriptId = string;
10
11export interface ViewExports {
12 default: Marko.Component;
13 meta: Meta | ((props: { ssr: true }) => Promise<Meta> | Meta);
14 theme?: css.Theme;
15}
16
17/**
18 * A filesystem object associated with some ID,
19 * such as a page's route to it's source file.
20 */
21export interface FileItem {
22 id: string;
23 file: string;
24}
25export interface Section {
26 root: string;
27 base?: string;
28}
29export interface Font {
30 name: string;
31 /**
32 * Specify either font name, file path, or URL to fetch it.
33 * Private fonts do not have a URL and will fail to build if missing.
34 */
35 sources: string[];
36 subsets: Array<{
37 vars?: Record<string, FontVariableAxis>;
38 layoutFeatures?: string[];
39 unicodes: FontRange | FontRange[];
40 /** example: `/myfont.[ext]` */
41 asset: string;
42 }>;
43}
44export type FontVars = Record<string, FontVariableAxis>;
45export type FontVariableAxis = number | [min: number, max: number];
46export type FontRange = string | number | { start: number; end: number };
47
48// TODO: self-host plausible for extra privacy. i am in a rush as i write this
49// TODO: properly locate this constant in `site.ts`
50//
51// I have this so I can see if the blog is actually seen by people. And for
52// understanding where paperclover.net gets its load for. When I need to scale
53// large enough, analytics will determine the location of the second web
54// hosting node.
55export const analytics = `<script async src="https://plausible.io/js/pa-Ajv81BZMkpbEE4QFznyoS.js"></script>
56<script>window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};plausible.init()</script> `;
57
58export function wrapDocument({
59 body,
60 head,
61 inlineCss,
62 scripts,
63}: {
64 head: string;
65 body: string;
66 inlineCss: string;
67 scripts: string;
68}) {
69 return `<!doctype html><html lang=en><head><meta charset=utf8>${head}${
70 inlineCss ? `<style>${inlineCss}</style>` : ""
71 }${analytics}</head><body>${body}${scripts ? `<script>${scripts}</script>` : ""}</body></html>`;
72}
73
74export function joinScripts(scriptSources: string[]) {
75 const { length } = scriptSources;
76 if (scriptSources[0] == null) return "";
77 if (length === 1) return UNWRAP(scriptSources[0]);
78 return "[" + scriptSources.map((source) => `()=>{${source}}`).join(",")
79 + "].map(e=>e())";
80}
81
82export { default as CloverScriptInclude } from "./add-script.marko";
83
84import { UNWRAP } from "@clo/lib/assert";
85import type * as incremental from "@clo/lib/incremental";
86import type { Meta } from "@clo/lib/meta";
87import type * as css from "../css.ts";