support for rss/calendar

This commit is contained in:
2023-05-20 19:08:43 -07:00
parent 5689c64c4e
commit e5f7af48cb
14 changed files with 782 additions and 90 deletions

View File

@@ -1,12 +1,15 @@
import { dirname, join as path_join } from 'path';
import { mkdirp, write_text } from '../fs';
import { icons } from '../icons';
import { BuildState } from './state';
import { load_partials, FrontMatter, Context, load_layout, render_template } from '../template';
import { render_theme_css_properties } from '../themes';
import { render_markdown_to_html, render_markdown_to_html_inline_sync } from '@doc-utils/markdown2html';
import { mkdirp, write_text } from '../fs';
import { CalendarConfig, RSSConfig } from '../conf';
import { render_theme_css_properties } from '../themes';
import { load_partials, FrontMatter, Context, load_layout, render_template } from '../template';
import { render_markdown_to_html, render_markdown_to_html_inline_sync } from '@doc-utils/markdown2html';
import { RSSEntry } from './rss';
import { DateTime } from 'luxon';
import { EventEntry } from './icalendar';
export interface OutFileURL {
base_url: string;
@@ -84,8 +87,12 @@ export function mustache_context(state: BuildState, page_url: string, frontmatte
page: frontmatter,
base_url: state.conf.base_url,
page_url: page_url,
site_title: state.conf.title,
author: get_author(state, frontmatter),
build_time: state.build_time,
icons: icons,
rss_feeds: state.conf.rss || [ ],
calendars: state.conf.calendars || [ ],
themes: Object.values(state.themes),
theme_groups: structuredClone(state.theme_groups),
markdown: {
@@ -102,7 +109,7 @@ export function mustache_context(state: BuildState, page_url: string, frontmatte
};
}
export async function render_page(state: BuildState, out_file: string, out_url: OutFileURL, text: string, render_as_markdown: boolean, frontmatter?: any) {
export async function render_page(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, text: string, render_as_markdown: boolean, frontmatter?: any) {
if (render_as_markdown) {
const opts = Object.assign({ }, state.conf.markdown, {
base_url: out_url.abs_url
@@ -126,70 +133,108 @@ export async function render_page(state: BuildState, out_file: string, out_url:
const context = mustache_context(state, out_url.abs_url, frontmatter);
const rendered = render_template(text, context, layout, structuredClone(state.partials), tags);
await write_text(out_file, rendered);
handle_page_side_effects(state, out_file, out_url, frontmatter);
handle_page_side_effects(state, in_file, out_file, out_url, text, frontmatter);
}
function handle_page_side_effects(state: BuildState, out_file: string, out_url: OutFileURL, frontmatter?: any) {
function handle_page_side_effects(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, text: string, frontmatter?: any) {
// Only actual HTML webpages are registered with things like the RSS feed; This is
// to prevent things like CSS files showing up, which may be templated (and therefore
// pass through this function), but are not really "pages"
if (frontmatter && typeof frontmatter === 'object' && out_file.endsWith('.html')) {
if (state.conf.rss) {
if (Array.isArray(state.conf.rss)) {
for (const rss_conf of state.conf.rss) {
handle_rss(state, rss_conf, out_url, frontmatter);
for (const [index, rss_conf] of Object.entries(state.conf.rss)) {
if (in_file.startsWith(rss_conf.in_dir + '/')) {
const entries = (state.rss[index] = state.rss[index] || [ ]);
handle_rss(state, rss_conf, entries, in_file, out_url, text, frontmatter);
}
}
else {
handle_rss(state, { }, out_url, frontmatter);
}
}
handle_sitemap(state, out_url, frontmatter);
handle_event(state, out_url, frontmatter);
if (state.conf.sitemap) {
handle_sitemap(state, out_url, frontmatter);
}
if (state.conf.calendars) {
for (const cal_conf of state.conf.calendars) {
handle_calendar(state, cal_conf, out_url, frontmatter);
if (frontmatter?.event) {
if (state.conf.events) {
handle_event(state, in_file, out_url, frontmatter);
}
if (state.conf.calendars) {
for (const [index, cal_conf] of Object.entries(state.conf.calendars)) {
if (in_file.startsWith(cal_conf.in_dir + '/')) {
const entries = (state.rss[index] = state.rss[index] || [ ]);
handle_calendar(state, cal_conf, entries, in_file, out_url, frontmatter);
}
}
}
}
}
}
function handle_rss(state: BuildState, rss_conf: RSSConfig, out_url: OutFileURL, frontmatter: any) {
// const field = rss_conf
// const rss_frontmatter = state.
// //
}
function handle_rss(state: BuildState, rss_conf: RSSConfig, entries: RSSEntry[], in_file: string, out_url: OutFileURL, text: string, frontmatter: FrontMatter) {
const author_or_authors = get_author(state, frontmatter);
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
function handle_sitemap(state: BuildState, out_url: OutFileURL, frontmatter: any) {
if (! state.conf.sitemap) {
return;
}
const sitemap_frontmatter = state.conf.sitemap.front_matter_field
? frontmatter?.[state.conf.sitemap.front_matter_field] || { }
: frontmatter?.sitemap || { };
state.sitemap.push({
entries.push({
url: out_url.abs_url,
lastmod: state.build_time.iso,
change_freq: sitemap_frontmatter?.change_freq,
priority: sitemap_frontmatter?.priority,
in_file: in_file,
html_content: text,
title: frontmatter?.title,
description: frontmatter?.description,
author_name: author?.name,
tags: frontmatter?.tags,
});
}
function handle_event(state: BuildState, out_url: OutFileURL, frontmatter: any) {
//
function handle_sitemap(state: BuildState, out_url: OutFileURL, frontmatter: FrontMatter) {
state.sitemap.push({
url: out_url.abs_url,
lastmod: state.build_time.iso,
change_freq: frontmatter?.sitemap?.change_freq,
priority: frontmatter?.sitemap?.priority,
});
}
function handle_calendar(state: BuildState, cal_conf: CalendarConfig, out_url: OutFileURL, frontmatter: any) {
//
function handle_event(state: BuildState, in_file: string, out_url: OutFileURL, frontmatter: FrontMatter) {
if (! state.conf.events) {
return;
}
const author_or_authors = get_author(state, frontmatter);
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
state.events.push({
url: out_url.abs_url,
in_file: in_file,
title: frontmatter.title,
description: frontmatter.description,
author_name: author?.name,
author_email: author?.email,
start_time: frontmatter.event?.start_time,
end_time: frontmatter.event?.end_time,
time_zone: frontmatter.event?.time_zone,
});
}
export function file_hash_matches(state: BuildState, in_file: string, new_hash: string) {
function handle_calendar(state: BuildState, cal_conf: CalendarConfig, entries: EventEntry[], in_file: string, out_url: OutFileURL, frontmatter: FrontMatter) {
const author_or_authors = get_author(state, frontmatter);
const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors;
entries.push({
url: out_url.abs_url,
in_file: in_file,
title: frontmatter.title,
description: frontmatter.description,
author_name: author?.name,
author_email: author?.email,
start_time: frontmatter.event?.start_time,
end_time: frontmatter.event?.end_time,
time_zone: frontmatter.event?.time_zone,
});
}
export function config_hash_matches(state: BuildState) {
const { old_metadata, new_metadata } = state;
if (! old_metadata.last_build?.config_hash) {
@@ -199,7 +244,11 @@ export function file_hash_matches(state: BuildState, in_file: string, new_hash:
if (old_metadata.last_build.config_hash !== new_metadata.last_build.config_hash) {
return false;
}
return true;
}
export function file_hash_matches(state: BuildState, in_file: string, new_hash: string) {
in_file = in_file.slice(state.conf.input.root.length);
const old_hash = state.old_metadata.files[in_file]?.last_build_hash;
@@ -217,7 +266,12 @@ export function file_hash_matches(state: BuildState, in_file: string, new_hash:
export function skip_file(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, frontmatter?: any) {
in_file = in_file.slice(state.conf.input.root.length);
state.new_metadata.files[in_file] = structuredClone(state.old_metadata?.files?.[in_file]);
handle_page_side_effects(state, out_file, out_url, frontmatter);
handle_page_side_effects(state, in_file, out_file, out_url, frontmatter);
}
export function copy_metadata(state: BuildState, in_file: string) {
in_file = in_file.slice(state.conf.input.root.length);
state.new_metadata.files[in_file] = structuredClone(state.old_metadata?.files?.[in_file]);
}
export function update_metadata(state: BuildState, in_file: string, hash: string) {
@@ -228,3 +282,23 @@ export function update_metadata(state: BuildState, in_file: string, hash: string
last_updated_time: state.build_time.iso,
};
}
export function get_author(state: BuildState, frontmatter?: FrontMatter) {
if (! frontmatter?.author) {
return null;
}
if (Array.isArray(frontmatter.author)) {
const list = frontmatter.author
.map((author) => state.conf.authors?.[author])
.filter((author) => author);
if (! list.length) {
return null;
}
return list;
}
return state.conf.authors?.[frontmatter.author]
}