"use strict"; /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createBlogFeedFiles = void 0; const tslib_1 = require("tslib"); const path_1 = tslib_1.__importDefault(require("path")); const fs_extra_1 = tslib_1.__importDefault(require("fs-extra")); const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger")); const feed_1 = require("feed"); const srcset = tslib_1.__importStar(require("srcset")); const utils_1 = require("@docusaurus/utils"); const utils_common_1 = require("@docusaurus/utils-common"); const cheerio_1 = require("cheerio"); async function generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale, }) { if (!blogPosts.length) { return null; } const { feedOptions, routeBasePath } = options; const { url: siteUrl, baseUrl, title, favicon } = siteConfig; const blogBaseUrl = (0, utils_1.normalizeUrl)([siteUrl, baseUrl, routeBasePath]); const blogPostsForFeed = feedOptions.limit === false || feedOptions.limit === null ? blogPosts : blogPosts.slice(0, feedOptions.limit); const updated = blogPostsForFeed[0]?.metadata.date; const feed = new feed_1.Feed({ id: blogBaseUrl, title: feedOptions.title ?? `${title} Blog`, updated, language: feedOptions.language ?? locale, link: blogBaseUrl, description: feedOptions.description ?? `${siteConfig.title} Blog`, favicon: favicon ? (0, utils_1.normalizeUrl)([siteUrl, baseUrl, favicon]) : undefined, copyright: feedOptions.copyright, }); const createFeedItems = options.feedOptions.createFeedItems ?? defaultCreateFeedItems; const feedItems = await createFeedItems({ blogPosts: blogPostsForFeed, siteConfig, outDir, defaultCreateFeedItems, }); feedItems.forEach(feed.addItem); return feed; } async function defaultCreateFeedItems({ blogPosts, siteConfig, outDir, }) { const { url: siteUrl } = siteConfig; function toFeedAuthor(author) { return { name: author.name, link: author.url, email: author.email }; } return Promise.all(blogPosts.map(async (post) => { const { metadata: { title: metadataTitle, permalink, date, description, authors, tags, }, } = post; const content = await (0, utils_1.readOutputHTMLFile)(permalink.replace(siteConfig.baseUrl, ''), outDir, siteConfig.trailingSlash); const $ = (0, cheerio_1.load)(content); const blogPostAbsoluteUrl = (0, utils_1.normalizeUrl)([siteUrl, permalink]); const toAbsoluteUrl = (src) => String(new URL(src, blogPostAbsoluteUrl)); // Make links and image urls absolute // See https://github.com/facebook/docusaurus/issues/9136 $(`div#${utils_common_1.blogPostContainerID} a, div#${utils_common_1.blogPostContainerID} img`).each((_, elm) => { if (elm.tagName === 'a') { const { href } = elm.attribs; if (href) { elm.attribs.href = toAbsoluteUrl(href); } } else if (elm.tagName === 'img') { const { src, srcset: srcsetAttr } = elm.attribs; if (src) { elm.attribs.src = toAbsoluteUrl(src); } if (srcsetAttr) { elm.attribs.srcset = srcset.stringify(srcset.parse(srcsetAttr).map((props) => ({ ...props, url: toAbsoluteUrl(props.url), }))); } } }); const feedItem = { title: metadataTitle, id: blogPostAbsoluteUrl, link: blogPostAbsoluteUrl, date, description, // Atom feed demands the "term", while other feeds use "name" category: tags.map((tag) => ({ name: tag.label, term: tag.label })), content: $(`#${utils_common_1.blogPostContainerID}`).html(), }; // json1() method takes the first item of authors array // it causes an error when authors array is empty const feedItemAuthors = authors.map(toFeedAuthor); if (feedItemAuthors.length > 0) { feedItem.author = feedItemAuthors; } return feedItem; })); } async function createBlogFeedFile({ feed, feedType, generatePath, }) { const [feedContent, feedPath] = (() => { switch (feedType) { case 'rss': return [feed.rss2(), 'rss.xml']; case 'json': return [feed.json1(), 'feed.json']; case 'atom': return [feed.atom1(), 'atom.xml']; default: throw new Error(`Feed type ${feedType} not supported.`); } })(); try { await fs_extra_1.default.outputFile(path_1.default.join(generatePath, feedPath), feedContent); } catch (err) { logger_1.default.error(`Generating ${feedType} feed failed.`); throw err; } } function shouldBeInFeed(blogPost) { const excluded = blogPost.metadata.frontMatter.draft || blogPost.metadata.frontMatter.unlisted; return !excluded; } async function createBlogFeedFiles({ blogPosts: allBlogPosts, options, siteConfig, outDir, locale, }) { const blogPosts = allBlogPosts.filter(shouldBeInFeed); const feed = await generateBlogFeed({ blogPosts, options, siteConfig, outDir, locale, }); const feedTypes = options.feedOptions.type; if (!feed || !feedTypes) { return; } await Promise.all(feedTypes.map((feedType) => createBlogFeedFile({ feed, feedType, generatePath: path_1.default.join(outDir, options.routeBasePath), }))); } exports.createBlogFeedFiles = createBlogFeedFiles;