Skip to content

Conversation

@macieklamberski
Copy link

@macieklamberski macieklamberski commented Oct 22, 2025

check list

  • Add test cases for the changes.
  • Passed the CI test.

Description

This PR replaces the current use of Feed library with Feedsmith to add support for hub links.

Recently, I stumbled upon the PR #240 and Issue #239, which has been pending due to lack of hub links support in the Feed library. As the creator of Feedsmith, I believe it could serve as a suitable alternative because:

  • It's flexible enough to cover all feed generation needs
  • It uses battle-tested fast-xml-parser for parsing and generating
  • Extra: Includes built-in feed format detection should you consider replacing the existing detectFeedType function

Regarding changes, I tried to maintain existing functionality with as minimal changes as possible to reduce confusion. If you'd be interested, I can make it more readable by breaking logic into smaller functions for easier maintanence/testing.

lib/generator.js Outdated
Comment on lines 10 to 13
return generateRssFeed({
title: config.title,
description: config.subtitle || config.description,
link: encodeURL(url),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some duplicated code across different formats. Is it possible to remove duplicate codes?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I applied some changes and described here: #249 (comment).

@stevenjoezhang stevenjoezhang mentioned this pull request Oct 24, 2025
2 tasks
@stevenjoezhang stevenjoezhang deleted the branch hexojs:master October 24, 2025 05:56
@stevenjoezhang stevenjoezhang changed the base branch from dev to master October 24, 2025 06:00
@coveralls
Copy link

Coverage Status

coverage: 98.611% (-1.4%) from 100.0%
when pulling 9849399 on macieklamberski:feat/feedsmith-integration
into 3fb6c3b on hexojs:master.

@stevenjoezhang stevenjoezhang linked an issue Oct 24, 2025 that may be closed by this pull request
3 tasks
@macieklamberski
Copy link
Author

Apologies for the amount of changes, but they're all focused on making the code more DRY.

The changes: 9ada4a7

TL;DR

  1. Moved logic of getting posts from module.exports to composePosts.
  2. Created composeFeed function to build the intermediate feed object.
  3. Moved code of building items into composeItem function.
  4. Then, these functions create intermediate objects passed down to dedicated generateRss and generateAtom.
  5. Created more tests for hub and author fields (can add more if needed).

Full

I reworked the lib/generator.js to split the code into smaller sub-functions. This was necessary because of the difference between Feed and Feedsmith.

  • Feed accepts shared (but limiting) object allowing to create Atom/RSS feeds.
  • Feedsmith requires creating dedicated objects for each feed format.

To DRY the code I also created intermediate functions (composeFeed, composeItem, composeItemContent, ...), then used them to generate the final objects passed down to functions generating each feed format.

The changes do not affect the existing functionality, tests implemented in the previous dev branch (with Feed integration) still work. I also added a few new ones.

I compiled 2 tables comparing source of the data used to generate feeds and put them at the end of this post.

Questions

  1. Comments field: The old template-based implementation included a <comments> element in RSS items when post.comments was truthy. This was removed in the dev branch. Was it an intentional change or limitation of Feed library? Should this be included in the refactored code?
  2. Feed updated date: The current version now matches master branch behavior (posts.first().updated || posts.first().date), but the dev branch didn't set this field explicitly, causing the Feed library to fall back to new Date() (current time). Was this change in dev intentional or accidental?

Old vs New Atom Feed

Field Master Branch Current Branch
Feed Level
title config.title ⚪ Same
icon config.feed.icon (via full_url_for) || gravatar(config.email) ⚪ Same
subtitle config.subtitle config.subtitle || config.description
link[rel=self] full_url_for(path) ⚪ Same
link[rel=hub] config.feed.hub ⚪ Same
link[rel=alternate] config.url (normalized) ⚪ Same
updated posts.first().updated || posts.first().date ⚪ Same
id config.url (normalized) ⚪ Same
author.name config.author ⚪ Same
author.email config.email ⚪ Same
generator 'Hexo' with uri='https://hexo.io/' ⚪ Same
rights 🟡 Not present config.author && 'All rights reserved ${year}, ${config.author}'
Entry Level
title post.title ⚪ Same
link post.permalink (via uriencode) full_url_for(post.permalink) with encodeURL
id post.permalink (via uriencode) ⚪ Same
published post.date ⚪ Same
updated post.updated || post.date ⚪ Same
content post.content.replace(/[\x00-\x1F\x7F]/g, '') (if config.feed.content) ⚪ Same
summary post.description || post.intro || post.excerpt || post.content.substring(0, config.feed.content_limit) post.description || post.intro || post.excerpt || post.content.substring(0, config.feed.content_limit || 140)
content[type=image] full_url_for(post.image) 🔴 Removed
link[rel=enclosure] 🟡 Not present full_url_for(post.image) (if post.image)
category post.categories.toArray() + post.tags.toArray() with term and scheme=permalink ⚪ Same
authors 🟡 Not present [{name: config.author, email: config.email}] (if config.author)

Old vs New RSS Feed

Field Master Branch Current Branch
Channel Level
title config.title ⚪ Same
link config.url (normalized) ⚪ Same
image.url config.feed.icon (via full_url_for) || gravatar(config.email) ⚪ Same
image.title config.title ⚪ Same
image.link config.url (normalized) ⚪ Same
atom:link[rel=self] full_url_for(path) with type='application/rss+xml' ⚪ Same
atom:link[rel=hub] config.feed.hub ⚪ Same
description config.description config.subtitle || config.description
pubDate posts.first().updated || posts.first().date ⚪ Same
generator 'http://hexo.io/' 'Hexo'
copyright 🟡 Not present config.author && 'All rights reserved ${year}, ${config.author}'
language 🟡 Not present config.language
Item Level
title post.title ⚪ Same
link post.permalink (via uriencode) full_url_for(post.permalink) with encodeURL
guid post.permalink (via uriencode) ⚪ Same
pubDate post.date ⚪ Same
description post.description || post.intro || post.excerpt || post.content.substring(0, config.feed.content_limit) post.description || post.intro || post.excerpt || post.content.substring(0, config.feed.content_limit || 140)
enclosure config.url + post.image (if post.image) full_url_for(post.image) (if post.image)
content:encoded post.content.replace(/[\x00-\x1F\x7F]/g, '') (if config.feed.content) ⚪ Same
category post.categories.toArray() + post.tags.toArray() with domain=config.url + category.path / tag.path post.categories.toArray() + post.tags.toArray() with domain=category.permalink / tag.permalink
author 🟡 Not present config.email (config.author) || config.email || config.author
comments post.permalink + '#disqus_thread' (if post.comments) 🔴 Removed

@stevenjoezhang stevenjoezhang changed the title feat: switch from Feed to Feedsmith library feat: switch to Feedsmith library Nov 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor Idea

4 participants