@bliztek/feed-generator
Version:
A simple and lightweight Node.js library for generating RSS 2.0, Atom, and JSON Feed formats.
162 lines (161 loc) • 6.67 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderAtom = void 0;
const xml_js_1 = require("../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: (0, xml_js_1.nodes)((0, xml_js_1.el)("name", a.name), a.email ? (0, xml_js_1.el)("email", a.email) : null, a.url ? (0, xml_js_1.el)("uri", a.url) : null),
}));
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: (0, xml_js_1.nodes)((0, xml_js_1.el)("title", item.title, { attributes: { type: "text" } }), ...entryLinks, (0, xml_js_1.el)("id", canonicalizeId(item.id)), (0, xml_js_1.el)("updated", entryUpdated), item.date ? (0, xml_js_1.el)("published", toIso(item.date)) : null, ...entryCategories, item.content
? (0, xml_js_1.el)("content", item.content, { attributes: { type: "html" } })
: null, item.summary
? (0, xml_js_1.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: (0, xml_js_1.nodes)((0, xml_js_1.el)("title", feed.title, { attributes: { type: "text" } }), feed.description
? (0, xml_js_1.el)("subtitle", feed.description, { attributes: { type: "text" } })
: null, feed.copyright
? (0, xml_js_1.el)("rights", feed.copyright, { attributes: { type: "text" } })
: null, ...feedLinks, (0, xml_js_1.el)("id", feedId), (0, xml_js_1.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="${(0, xml_js_1.escapeXml)(stylesheet)}"`
: "";
return `${(0, xml_js_1.xmlDeclaration)()}${stylesheetPI}${sep}${(0, xml_js_1.renderXml)([feedNode], 0, compact)}`;
};
exports.renderAtom = renderAtom;