159 lines
3.7 KiB
TypeScript
159 lines
3.7 KiB
TypeScript
|
|
import { AuthorConfig, CalendarConfig, Config, RSSConfig } from './conf';
|
|
import { render as mustache_render } from 'mustache';
|
|
import { promises as fs } from 'fs';
|
|
import { resolve as resolve_path } from 'path';
|
|
import { glob } from 'glob';
|
|
import { load_from_dir } from './fs';
|
|
import { ColorTheme } from '@doc-utils/color-themes';
|
|
import { ThemeGroups } from './build-files';
|
|
import { ChangeFreq } from './build-files/sitemap';
|
|
|
|
export interface Context {
|
|
env?: Record<string, string>;
|
|
page?: FrontMatter;
|
|
base_url: string;
|
|
page_url: string;
|
|
page_published: ContextTime;
|
|
page_updated: ContextTime;
|
|
site_title: string;
|
|
author: AuthorConfig | AuthorConfig[];
|
|
event?: ContextEvent | ContextEvent[];
|
|
event_series?: {
|
|
start?: ContextTime;
|
|
end?: ContextTime;
|
|
};
|
|
icons: Record<string, string>;
|
|
themes: ColorTheme[];
|
|
theme_groups: ThemeGroups;
|
|
rss_feeds: RSSConfig[];
|
|
calendars: CalendarConfig[];
|
|
build_time: ContextTime;
|
|
markdown: {
|
|
render_inline(): MustacheRenderer;
|
|
}
|
|
}
|
|
|
|
export interface ContextTime {
|
|
iso: string;
|
|
rfc2822: string;
|
|
html: string;
|
|
}
|
|
|
|
export interface ContextEvent {
|
|
title?: string;
|
|
start?: ContextTime;
|
|
end?: ContextTime;
|
|
time_zone?: `${string}/${string}`;
|
|
location?: ContextLocation;
|
|
}
|
|
|
|
export interface ContextLocation {
|
|
description?: string;
|
|
lat?: string;
|
|
long?: string;
|
|
// todo: represent this better?
|
|
address?: string;
|
|
}
|
|
|
|
export interface FrontMatter {
|
|
skip?: boolean;
|
|
layout?: string;
|
|
title?: string;
|
|
description?: string;
|
|
tags?: string[];
|
|
author?: string | string[];
|
|
rss?: RSSFrontmatter;
|
|
sitemap?: SitemapFrontmatter;
|
|
event?: EventFrontmatter | EventFrontmatter[];
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export interface SitemapFrontmatter {
|
|
skip?: boolean;
|
|
change_freq?: ChangeFreq;
|
|
priority?: number;
|
|
}
|
|
|
|
export interface EventFrontmatter {
|
|
title?: string;
|
|
start?: string;
|
|
end?: string;
|
|
time_zone?: `${string}/${string}`;
|
|
location?: FrontMatterLocation;
|
|
}
|
|
|
|
export interface FrontMatterLocation {
|
|
description?: string;
|
|
lat?: string;
|
|
long?: string;
|
|
// todo: represent this better?
|
|
address?: string;
|
|
}
|
|
|
|
export interface RSSFrontmatter {
|
|
skip?: boolean;
|
|
}
|
|
|
|
export function render_template(template: string, context: Context, layout?: string, partials: Record<string, string> = { }, tags?: [ string, string ]) {
|
|
partials['.content'] = template;
|
|
return mustache_render(layout || template, context, partials, tags);
|
|
}
|
|
|
|
export async function load_extras() {
|
|
const extras: Record<string, string> = Object.create(null);
|
|
const extras_dir = resolve_path(__dirname, '../extras');
|
|
const extras_files = [
|
|
'svg-links.js',
|
|
'components/color-scheme-toggle-button.js',
|
|
'components/outline-button.js',
|
|
'components/outline-inline.js',
|
|
'prism.css',
|
|
'typography/spacious.css',
|
|
'typography/compact.css',
|
|
'typography/general.css',
|
|
'theme-animation.css',
|
|
'forms-inputs/spacious.css',
|
|
'forms-inputs/compact.css',
|
|
'forms-inputs/general.css',
|
|
'figures.css',
|
|
];
|
|
|
|
const promises = extras_files.map((file) => load_from_dir(extras_dir, file));
|
|
|
|
for (let i = 0; i < extras_files.length; i++) {
|
|
extras[`.extras/${extras_files[i]}`] = await promises[i];
|
|
}
|
|
|
|
return extras;
|
|
}
|
|
|
|
export async function load_layout(conf: Config, file: string) {
|
|
return load_from_dir(conf.templates?.layouts, file);
|
|
}
|
|
|
|
export async function load_partials(conf: Config) {
|
|
const path = conf.templates?.partials;
|
|
|
|
if (! path) {
|
|
return { };
|
|
}
|
|
|
|
const partials: Record<string, string> = { };
|
|
const partial_files = await glob(path + '/**/*', {
|
|
cwd: path,
|
|
absolute: false,
|
|
});
|
|
|
|
for (const file of partial_files) {
|
|
const abs_file = resolve_path(path, file);
|
|
partials[file] = await fs.readFile(abs_file, 'utf8');
|
|
}
|
|
|
|
return partials;
|
|
}
|
|
|
|
export interface MustacheRenderer {
|
|
(this: void, text: string, render: (text: string) => string): string;
|
|
}
|