import debugUtil from "debug";
import pkg from "../package.json" with {type: "json"};
import { DeepCopy } from "@11ty/eleventy-utils";
import rssPlugin from "./rssPlugin.js";
const debug = debugUtil("Eleventy:Rss:Feed");
function getFeedContent({ type, stylesheet, collection, script }) {
// Note: page.lang comes from the i18n plugin: https://www.11ty.dev/docs/plugins/i18n/#page.lang
if(type === "rss") {
// Nunjucks template
return `
${stylesheet ? `\n` : ""}
${script ? `` : ""}
{{ metadata.title }}
{{ metadata.base | addPathPrefixToFullUrl }}
{{ metadata.subtitle }}
{{ metadata.language or page.lang }}
{%- if metadata.icon %}{{ metadata.icon }}{%- endif %}
{%- for post in collections.${collection.name} | reverse | eleventyFeedHead(${collection.limit}) %}
{%- set absolutePostUrl = post.url | htmlBaseUrl(metadata.base) %}
-
{{ post.data.title }}
{{ absolutePostUrl }}
{%- if (post.data.summary) -%}
{{ post.data.summary }}
{{ post.content | renderTransforms(post.data.page, metadata.base) }}
{%- else -%}
{{ post.content | renderTransforms(post.data.page, metadata.base) }}
{%- endif -%}
{{ post.date | dateToRfc822 }}
{{ metadata.author.name }}
{{ absolutePostUrl }}
{%- endfor %}
`;
}
if(type === "atom") {
// Nunjucks template
return `
${stylesheet ? `\n` : ""}
${script ? `` : ""}
{{ metadata.title }}
{{ metadata.subtitle }}
{{ collections['${collection.name}'] | getNewestCollectionItemDate | dateToRfc3339 }}
{{ metadata.base | addPathPrefixToFullUrl }}
{%- if metadata.icon %}
{{ metadata.icon }}
{%- endif %}
{%- if metadata.logo %}
{{ metadata.logo }}
{%- endif %}
{{ metadata.author.name }}
{%- if metadata.author.email %}
{{ metadata.author.email }}
{%- endif %}
{%- for post in collections['${collection.name}'] | reverse | eleventyFeedHead(${collection.limit}) %}
{%- set absolutePostUrl %}{{ post.url | htmlBaseUrl(metadata.base) }}{% endset %}
{{ post.data.title }}
{{ post.date | dateToRfc3339 }}
{{ absolutePostUrl }}
{%- if post.data.summary %}
{{ post.data.summary }}
{%- endif %}
{{ post.content | renderTransforms(post.data.page, metadata.base) }}
{%- endfor %}
`;
}
if(type === "json") {
return `{
"version": "https://jsonfeed.org/version/1.1",
"title": "{{ metadata.title }}",
"language": "{{ metadata.language or page.lang }}",
"home_page_url": "{{ metadata.base | addPathPrefixToFullUrl }}",
"feed_url": "{{ permalink | htmlBaseUrl(metadata.base) }}",
"description": "{{ metadata.description }}",
"authors": [
{
"name": "{{ metadata.author.name }}"{% if metadata.author.email %},
"url": "mailto:{{ metadata.author.email }}"
{%- endif %}
}
],
"items": [
{%- for post in collections['${collection.name}'] | reverse | eleventyFeedHead(${collection.limit}) %}
{%- set absolutePostUrl %}{{ post.url | htmlBaseUrl(metadata.base) }}{% endset %}
{
"id": "{{ absolutePostUrl }}",
"url": "{{ absolutePostUrl }}",
"title": "{{ post.data.title }}",
"content_html": {% if post.content %}{{ post.content | renderTransforms(post.data.page, metadata.base) | dump | safe }}{% else %}""{% endif %},
"date_published": "{{ post.date | dateToRfc3339 }}"
}
{% if not loop.last %},{% endif %}
{%- endfor %}
]
}`
}
throw new Error("Missing or invalid feed type. Received: " + type);
}
export default function eleventyFeedPlugin(eleventyConfig, options = {}) {
eleventyConfig.versionCheck(pkg["11ty"].compatibility);
// Guaranteed unique, first add wins
const pluginHtmlBase = eleventyConfig.resolvePlugin("@11ty/eleventy/html-base-plugin");
eleventyConfig.addPlugin(pluginHtmlBase, options.htmlBasePluginOptions || {});
// Guaranteed unique, first add wins
eleventyConfig.addPlugin(rssPlugin, options.rssPluginOptions || {});
let slugifyFilter = eleventyConfig.getFilter("slugify");
let inputPathSuffix = options?.metadata?.title ? `-${slugifyFilter(options?.metadata?.title)}` : "";
options = DeepCopy({
// rss and json also supported
type: "atom",
collection: {
name: false, // required
limit: 0, // limit number of entries, 0 means no limit
},
outputPath: "/feed.xml",
inputPath: `eleventy-plugin-feed${inputPathSuffix}-${options.type || "atom"}.njk`, // TODO make this more unique
templateData: {},
metadata: {
title: "Blog Title",
subtitle: "This is a longer description about your blog.",
language: "", // downstream templates use `page.lang` as fallback
base: "https://example.com/",
author: {
name: "Your Name",
email: "", // Optional
}
}
}, options);
if(!options.collection?.name) {
throw new Error("Missing `collection.name` option in feedPlugin from @11ty/eleventy-plugin-rss.");
}
if(typeof options.collection?.name !== "string") {
throw new Error("Only string is supported in `collection.name` option in feedPlugin from @11ty/eleventy-plugin-rss. Received: " + typeof options.collection?.name);
}
let eleventyExcludeFromCollections;
let eleventyImport;
if(options.collection.name === "all") {
eleventyExcludeFromCollections = true;
eleventyImport = {};
} else {
eleventyExcludeFromCollections = [ options.collection.name ]
eleventyImport = {
collections: [ options.collection.name ],
};
}
let templateData = {
...options?.templateData || {},
permalink: options.outputPath,
eleventyExcludeFromCollections,
eleventyImport,
layout: false,
metadata: options.metadata,
};
// Get the first `n` elements of a collection.
eleventyConfig.addFilter("eleventyFeedHead", function(array, n) {
if(!n || n === 0) {
return array;
}
if(n < 0) {
return array.slice(n);
}
return array.slice(0, n);
});
eleventyConfig.addTemplate(options.inputPath, getFeedContent(options), templateData);
};
Object.defineProperty(eleventyFeedPlugin, "eleventyPackage", {
value: `${pkg.name}/feed-plugin`
});
Object.defineProperty(eleventyFeedPlugin, "eleventyPluginOptions", {
value: {
// multiple adds of this one is OK
unique: false
}
});