145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
|
|
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, });
|
|
}
|