import type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'; import { create as create_xml } from 'xmlbuilder2'; import { BuildState } from './state'; import { write_text } from '../fs'; import { AuthorConfig, app_version } from '../conf'; import { map_output_file_to_url } from './helpers'; import { DateTime } from 'luxon'; import { FileMetadata } from '../metadata'; export interface RSSEntry { url: string; in_file: string; title?: string; description?: string; authors?: AuthorConfig[]; html_content?: string; tags?: string[]; } export async function write_rss_if_needed(state: BuildState) { if (! state.conf.rss) { return; } for (let index = 0; index < state.conf.rss.length; index++) { const rss_conf = state.conf.rss[index]; const doc = create_xml({ version: '1.0', encoding: 'UTF-8' }); const { abs_url: self_url } = map_output_file_to_url(state, rss_conf.out_file); if (rss_conf.xsl) { for (const url of rss_conf.xsl) { doc.ins('xml-stylesheet', `href="${url}" type="text/xsl"`); } } const rss = doc.ele('rss', { version: '2.0', 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', 'xmlns:content': 'http://purl.org/rss/1.0/modules/content/', 'xmlns:atom': 'http://www.w3.org/2005/Atom', // 'xmlns:docs2website': 'urn:uuid:7fc4e5d4-f68e-11ed-b0d0-00155ddef564', }); const channel = rss.ele('channel'); channel.ele('generator').txt(`docs2website ${app_version}`); if (rss_conf.title) { channel.ele('title').txt(rss_conf.title); } if (rss_conf.link) { channel.ele('link').txt(rss_conf.link); } link(channel, 'application/rss+xml', 'self', self_url); if (rss_conf.alternates?.length) { for (const alt of rss_conf.alternates) { link(channel, alt.type, 'alternate', alt.href); } } if (rss_conf.description) { channel.ele('description').txt(rss_conf.description); } if (rss_conf.language) { channel.ele('language').txt(rss_conf.language); } if (rss_conf.copyright) { channel.ele('copyright').txt(rss_conf.copyright); } channel.ele('lastBuildDate').txt(state.build_time.rfc2822); // channel.ele('rating').txt(''); // see: https://www.w3.org/PICS/ // channel.ele('pubDate').txt(publish_date.toUTCString()); // channel.ele('categories').txt(''); // channel.ele('docs').txt(''); // channel.ele('managingEditor').txt('james@jbrumond.me'); // channel.ele('webMaster').txt('james@jbrumond.me'); // channel.ele('ttl').txt(''); // channel.ele('image').txt(''); // channel.ele('skipHours').txt(''); // channel.ele('skipDays').txt(''); let entries: (RSSEntry & { metadata: FileMetadata, first_seen: number })[] = [ ]; for (const entry of state.rss[index] || [ ]) { const in_file = entry.in_file.slice(state.conf.input.root.length); const metadata = state.new_metadata.files[in_file]; entries.push({ ...entry, metadata, first_seen: DateTime.fromISO(metadata.first_seen_time).toUnixInteger() }); } entries = entries.sort((a, b) => { return b.first_seen - a.first_seen; }); for (const entry of entries) { const item = channel.ele('item'); item.ele('link').txt(entry.url); if (entry.title) { item.ele('title').txt(entry.title); } if (entry.description) { item.ele('description').txt(entry.description); } if (entry.authors) { for (const author of entry.authors) { item.ele('dc:creator').ele({ $: author.name || author.url || author.email }); } } if (entry.tags) { entry.tags.forEach((tag) => item.ele('category').txt(tag)); } item.ele('guid').txt(entry.url); item.ele('pubDate').txt(DateTime.fromISO(entry.metadata.first_seen_time).toRFC2822()); if (entry.html_content) { item.ele('content:encoded').ele({ $: entry.html_content }); } } const xml = doc.toString({ indent: ' ', prettyPrint: true, }); await write_text(rss_conf.out_file, xml); } } function link(channel: XMLBuilder, type: string, rel: string, href: string) { // channel.ele('link', { href, rel, type, }); channel.ele('atom:link', { type, rel, href, }); }