feat(feed): Support custom feedUrl for feeds and use it as the self-link in RSS/Atom/JSON; update docs

This commit is contained in:
2025-11-01 03:35:45 +00:00
parent 289483ba0a
commit 4ed892ddc2
5 changed files with 45 additions and 5 deletions

View File

@@ -1,5 +1,14 @@
# Changelog
## 2025-11-01 - 1.4.0 - feat(feed)
Support custom feedUrl for feeds and use it as the self-link in RSS/Atom/JSON; update docs
- Add optional feedUrl option to IFeedOptions (ts/classes.feed.ts).
- Use feedUrl as the atom:link rel="self" href in RSS and as the <link rel="self"> in Atom when provided.
- Expose feedUrl as the JSON Feed "feed_url" value (ts/classes.feed.ts).
- PodcastFeed now uses podcastOptions.feedUrl for its atom self-link (ts/classes.podcast.ts).
- Update README: add a Custom Feed URL section and mention feedUrl in the API docs (readme.md).
## 2025-10-31 - 1.3.0 - feat(parsing)
Replace rss-parser with fast-xml-parser and add native feed parser; update Smartfeed to use parseFeedXML and adjust plugins/tests

View File

@@ -7,6 +7,7 @@
## Features ✨
- 🎯 **Full TypeScript Support** - Complete type definitions for all feed formats
- 🌐 **Cross-Platform** - Works in Node.js, Bun, Deno, and browsers
- 📡 **Multiple Feed Formats** - RSS 2.0, Atom 1.0, JSON Feed 1.0, and Podcast RSS
- 🎙️ **Modern Podcast Support** - iTunes tags, Podcast 2.0 namespace (guid, medium, locked, persons, transcripts, funding)
- 🔒 **Built-in Validation** - Comprehensive validation for URLs, emails, domains, and timestamps
@@ -57,6 +58,30 @@ const atom = feed.exportAtomFeed();
const json = feed.exportJsonFeed();
```
### Custom Feed URL
You can specify a custom URL for your feed's self-reference instead of the default `https://${domain}/feed.xml`:
```typescript
const feed = smartfeed.createFeed({
domain: 'example.com',
title: 'My Blog',
description: 'Latest posts',
category: 'Technology',
company: 'Example Inc',
companyEmail: 'hello@example.com',
companyDomain: 'https://example.com',
feedUrl: 'https://cdn.example.com/feeds/main.xml' // Custom feed URL
});
// The feedUrl will be used in:
// - RSS: <atom:link href="..." rel="self">
// - Atom: <link href="..." rel="self">
// - JSON Feed: "feed_url" field
```
This is particularly useful when your feed is hosted on a CDN or different domain than your main site.
### Creating a Podcast Feed
```typescript
@@ -183,6 +208,7 @@ Creates a standard feed (RSS/Atom/JSON).
- `company` (string) - Company/organization name
- `companyEmail` (string) - Contact email
- `companyDomain` (string) - Company website URL (absolute)
- `feedUrl` (string, optional) - Custom URL for the feed's self-reference (defaults to `https://${domain}/feed.xml`)
#### `createPodcastFeed(options: IPodcastFeedOptions): PodcastFeed`

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartfeed',
version: '1.3.0',
version: '1.4.0',
description: 'A library for creating and parsing various feed formats.'
}

View File

@@ -19,6 +19,8 @@ export interface IFeedOptions {
companyEmail: string;
/** The company website URL (must be absolute) */
companyDomain: string;
/** Optional: Custom URL for the feed's atom:link rel="self" (defaults to https://${domain}/feed.xml) */
feedUrl?: string;
}
/**
@@ -186,7 +188,8 @@ export class Feed {
rss += `<category>${this.escapeXml(this.options.category)}</category>\n`;
// Atom self link
rss += `<atom:link href="https://${this.options.domain}/feed.xml" rel="self" type="application/rss+xml" />\n`;
const selfUrl = this.options.feedUrl || `https://${this.options.domain}/feed.xml`;
rss += `<atom:link href="${selfUrl}" rel="self" type="application/rss+xml" />\n`;
// Items
for (const item of this.items) {
@@ -221,7 +224,8 @@ export class Feed {
atom += `<title>${this.escapeXml(this.options.title)}</title>\n`;
atom += `<subtitle>${this.escapeXml(this.options.description)}</subtitle>\n`;
atom += `<link href="https://${this.options.domain}" />\n`;
atom += `<link href="https://${this.options.domain}/feed.xml" rel="self" />\n`;
const selfUrl = this.options.feedUrl || `https://${this.options.domain}/feed.xml`;
atom += `<link href="${selfUrl}" rel="self" />\n`;
atom += `<updated>${this.formatIso8601Date(new Date())}</updated>\n`;
atom += `<generator>@push.rocks/smartfeed</generator>\n`;
atom += '<author>\n';
@@ -265,7 +269,7 @@ export class Feed {
version: 'https://jsonfeed.org/version/1',
title: this.options.title,
home_page_url: `https://${this.options.domain}`,
feed_url: `https://${this.options.domain}/feed.json`,
feed_url: this.options.feedUrl || `https://${this.options.domain}/feed.json`,
description: this.options.description,
icon: '',
favicon: '',

View File

@@ -369,7 +369,8 @@ export class PodcastFeed extends Feed {
rss += `<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>\n`;
// Atom self link
rss += `<atom:link href="https://${this.podcastOptions.domain}/feed.xml" rel="self" type="application/rss+xml" />\n`;
const selfUrl = this.podcastOptions.feedUrl || `https://${this.podcastOptions.domain}/feed.xml`;
rss += `<atom:link href="${selfUrl}" rel="self" type="application/rss+xml" />\n`;
// iTunes channel tags
rss += `<itunes:author>${this.escapeXml(this.podcastOptions.itunesAuthor)}</itunes:author>\n`;