UNPKG

@bliztek/feed-generator

Version:

A simple and lightweight Node.js library for generating RSS 2.0, Atom, and JSON Feed formats.

158 lines (157 loc) 6.29 kB
import { xmlDeclaration, renderXml, el, nodes, escapeXml } from "../xml.js"; const inferImageType = (url) => { var _a; const ext = (_a = url.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase(); const types = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", svg: "image/svg+xml", avif: "image/avif", }; return ext ? types[ext] : undefined; }; const toIso = (date) => new Date(date).toISOString(); /** Ensure a URL used as an Atom ID is in canonical form (trailing slash on origin-only URLs). */ const canonicalizeId = (url) => { try { const u = new URL(url); if (u.pathname === "" || u.pathname === "/") { u.pathname = "/"; return u.toString(); } return url; } catch (_a) { return url; } }; const renderAuthors = (authors) => (authors !== null && authors !== void 0 ? authors : []).map((a) => ({ tag: "author", children: nodes(el("name", a.name), a.email ? el("email", a.email) : null, a.url ? el("uri", a.url) : null), })); export const renderAtom = (feed, options = { compact: false }) => { var _a, _b, _c, _d, _e, _f, _g; const { compact, stylesheet } = options; // feed.updated is already derived by prepareFeed in index.ts const feedUpdated = feed.updated ? toIso(feed.updated) : new Date().toISOString(); const feedId = canonicalizeId((_a = feed.id) !== null && _a !== void 0 ? _a : feed.link); const feedLinks = []; if ((_b = feed.feedLinks) === null || _b === void 0 ? void 0 : _b.atom) { feedLinks.push({ tag: "link", selfClosing: true, attributes: { href: feed.feedLinks.atom, rel: "self", type: "application/atom+xml", }, }); } feedLinks.push({ tag: "link", selfClosing: true, attributes: { href: feed.link, rel: "alternate", type: "text/html" }, }); if (feed.nextUrl) { feedLinks.push({ tag: "link", selfClosing: true, attributes: { href: feed.nextUrl, rel: "next" }, }); } if ((_c = feed.feedLinks) === null || _c === void 0 ? void 0 : _c.hub) { feedLinks.push({ tag: "link", selfClosing: true, attributes: { href: feed.feedLinks.hub, rel: "hub" }, }); } const categoryNodes = ((_d = feed.categories) !== null && _d !== void 0 ? _d : []).map((cat) => ({ tag: "category", selfClosing: true, attributes: { term: cat.name, scheme: cat.domain, label: cat.label, }, })); const entryNodes = feed.items.map((item) => { var _a, _b, _c, _d; const entryUpdated = item.updated ? toIso(item.updated) : item.date ? toIso(item.date) : feedUpdated; const entryLinks = []; if (item.link) { entryLinks.push({ tag: "link", selfClosing: true, attributes: { href: item.link, rel: "alternate", type: "text/html" }, }); } ((_a = item.enclosures) !== null && _a !== void 0 ? _a : []).forEach((enc) => { entryLinks.push({ tag: "link", selfClosing: true, attributes: { href: enc.url, rel: "enclosure", type: enc.type, length: enc.length, }, }); }); if (item.image) { entryLinks.push({ tag: "link", selfClosing: true, attributes: { href: item.image, rel: "enclosure", type: inferImageType(item.image), }, }); } const entryCategories = ((_b = item.categories) !== null && _b !== void 0 ? _b : []).map((cat) => ({ tag: "category", selfClosing: true, attributes: { term: cat.name, scheme: cat.domain, label: cat.label, }, })); return { tag: "entry", children: nodes(el("title", item.title, { attributes: { type: "text" } }), ...entryLinks, el("id", canonicalizeId(item.id)), el("updated", entryUpdated), item.date ? el("published", toIso(item.date)) : null, ...entryCategories, item.content ? el("content", item.content, { attributes: { type: "html" } }) : null, item.summary ? el("summary", item.summary, { attributes: { type: "text" } }) : null, ...renderAuthors(item.authors), ...((_d = (_c = item.extensions) === null || _c === void 0 ? void 0 : _c.xmlElements) !== null && _d !== void 0 ? _d : [])), }; }); const generatorNode = feed.generator ? { tag: "generator", children: [feed.generator] } : null; const feedAttrs = { xmlns: "http://www.w3.org/2005/Atom", }; if ((_e = feed.extensions) === null || _e === void 0 ? void 0 : _e.xmlNamespaces) { Object.assign(feedAttrs, feed.extensions.xmlNamespaces); } const feedNode = { tag: "feed", attributes: feedAttrs, children: nodes(el("title", feed.title, { attributes: { type: "text" } }), feed.description ? el("subtitle", feed.description, { attributes: { type: "text" } }) : null, feed.copyright ? el("rights", feed.copyright, { attributes: { type: "text" } }) : null, ...feedLinks, el("id", feedId), el("updated", feedUpdated), generatorNode, ...categoryNodes, ...renderAuthors(feed.authors), ...((_g = (_f = feed.extensions) === null || _f === void 0 ? void 0 : _f.xmlElements) !== null && _g !== void 0 ? _g : []), ...entryNodes), }; const sep = compact ? "" : "\n"; const stylesheetPI = stylesheet ? `${sep}<?xml-stylesheet type="text/xsl" href="${escapeXml(stylesheet)}"?>` : ""; return `${xmlDeclaration()}${stylesheetPI}${sep}${renderXml([feedNode], 0, compact)}`; };