diff --git a/scripts/data/enhance-feed.js b/scripts/data/enhance-feed.js new file mode 100644 index 00000000..874b64a1 --- /dev/null +++ b/scripts/data/enhance-feed.js @@ -0,0 +1,96 @@ +'use strict' + +/** + * Enhances the RSS feed to include items from special content pages + * (release notes, ecosystem content, etc.) that are stored as YAML + * arrays in frontmatter rather than individual markdown files. + */ + +const fs = require('fs') +const path = require('path') +const xml2js = require('xml2js') +const matter = require('gray-matter') +const dayjs = require('dayjs') + +const xmlFilePath = 'dist/index.xml' + +// Only include items published after this date (to avoid backfilling old content) +const CUTOFF_DATE = dayjs('2025-11-25') + +// Content types to include in the unified feed +// Each item in the data array should have: title, date, path (URL) +const CONTENT_SOURCES = [ + { file: 'releasenotes.md', category: 'Release Notes' }, + { file: 'ecosystemcontent.md', category: 'Ecosystem' }, + { file: 'newscoverage.md', category: 'News Coverage' }, + { file: 'videos.md', category: 'Videos' }, + { file: 'tutorials.md', category: 'Tutorials' }, + { file: 'events.md', category: 'Events' }, +] + +function parseContentFile(filename) { + const filepath = path.resolve('src/_blog', filename) + try { + const content = fs.readFileSync(filepath, 'utf8') + const { data } = matter(content) + const now = dayjs() + return (data.data || []).filter((item) => { + if (item.hidden) return false + if (item.publish_date && dayjs(item.publish_date).isAfter(now)) return false + return dayjs(item.publish_date || item.date).isAfter(CUTOFF_DATE) + }) + } catch (err) { + console.error(`Warning: Could not read ${filename}:`, err.message) + return [] + } +} + +function itemToRssEntry(item, category) { + return { + title: [item.title], + link: [item.path], + pubDate: [dayjs(item.date).toDate().toUTCString()], + description: [item.title], + category: [category], + guid: [{ _: item.path, $: { isPermaLink: 'true' } }], + } +} + +async function enhanceFeed() { + let xmlData, parsed + try { + xmlData = fs.readFileSync(xmlFilePath, 'utf8') + parsed = await xml2js.parseStringPromise(xmlData) + } catch (err) { + console.error('Could not read/parse RSS feed:', err.message) + process.exit(1) + } + + // Blog posts link to the blog domain, special content links externally + const blogDomain = parsed.rss.channel[0].link[0] + const existingItems = (parsed.rss.channel[0].item || []).filter( + (item) => item.link[0].startsWith(blogDomain) + ) + + // Parse special content and convert to RSS items + const additionalItems = CONTENT_SOURCES.flatMap((source) => + parseContentFile(source.file).map((i) => itemToRssEntry(i, source.category)) + ) + + // Deduplicate by guid + const seen = new Set() + const allItems = [...existingItems, ...additionalItems] + .filter((item) => { + const guid = item.guid?.[0]?._ || item.guid?.[0] || item.link[0] + return !seen.has(guid) && seen.add(guid) + }) + .sort((a, b) => new Date(b.pubDate[0]) - new Date(a.pubDate[0])) + + parsed.rss.channel[0].item = allItems + + const builder = new xml2js.Builder({ xmldec: { version: '1.0', encoding: 'UTF-8' } }) + fs.writeFileSync(xmlFilePath, builder.buildObject(parsed)) + console.log(`Enhanced RSS feed: ${allItems.length} items (${existingItems.length} posts + ${additionalItems.length} special)`) +} + +exports.enhanceFeed = enhanceFeed diff --git a/scripts/data/index.js b/scripts/data/index.js index a7861ce4..b99abfee 100755 --- a/scripts/data/index.js +++ b/scripts/data/index.js @@ -2,10 +2,15 @@ 'use strict' +const { enhanceFeed } = require('./enhance-feed') const { generateIndexFile } = require('./latest-posts') const { generateNewsFile } = require('./latest-news') const { generateVideosFile } = require('./latest-videos') -generateIndexFile() -generateNewsFile() -generateVideosFile() +// Enhance RSS feed first (adds release notes, ecosystem content, etc.) +// then generate index.json from the enhanced feed +enhanceFeed().then(() => { + generateIndexFile() + generateNewsFile() + generateVideosFile() +}) diff --git a/src/_blog/ecosystemcontent.md b/src/_blog/ecosystemcontent.md index 0d346756..62911e9d 100644 --- a/src/_blog/ecosystemcontent.md +++ b/src/_blog/ecosystemcontent.md @@ -5,7 +5,7 @@ sitemap: exclude: true data: - title: 'Provide Sweep: Solving the DHT Bottleneck for Self-Hosting IPFS at Scale' - date: 2025-11-27 + date: 2025-11-26 publish_date: card_image: /blog-post-placeholder.png path: https://ipshipyard.com/blog/2025-dht-provide-sweep/