Skip to content

Commit 50dd88b

Browse files
fix(@astrojs/rss): prevent an error when pubDate is missing (#12137)
* fix(rss): prevent error when `pubDate` is missing * docs(rss): switch some RSS items to optional
1 parent 2385d58 commit 50dd88b

6 files changed

Lines changed: 37 additions & 10 deletions

File tree

.changeset/ninety-dolls-reply.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/rss': patch
3+
---
4+
5+
Fixes an error that occurred when the optional `pubDate` property was missing in an item.

.changeset/plenty-kings-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/rss': patch
3+
---
4+
5+
Fixes an error where docs incorrectly stated the `title`, `link` and `pubDate` properties of RSS items was required.

packages/astro-rss/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,27 +198,27 @@ const item = {
198198
199199
### `title`
200200
201-
Type: `string (required)`
201+
Type: `string (optional)`
202202
203-
The title of the item in the feed.
203+
The title of the item in the feed. Optional only if a description is set. Otherwise, required.
204204
205205
### `link`
206206
207-
Type: `string (required)`
207+
Type: `string (optional)`
208208
209209
The URL of the item on the web.
210210
211211
### `pubDate`
212212
213-
Type: `Date (required)`
213+
Type: `Date (optional)`
214214
215215
Indicates when the item was published.
216216
217217
### `description`
218218
219219
Type: `string (optional)`
220220
221-
A synopsis of your item when you are publishing the full content of the item in the `content` field. The `description` may alternatively be the full content of the item in the feed if you are not using the `content` field (entity-coded HTML is permitted).
221+
A synopsis of your item when you are publishing the full content of the item in the `content` field. The `description` may alternatively be the full content of the item in the feed if you are not using the `content` field (entity-coded HTML is permitted). Optional only if a title is set. Otherwise, required.
222222
223223
### `content`
224224

packages/astro-rss/src/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ export const rssSchema = z.object({
55
description: z.string().optional(),
66
pubDate: z
77
.union([z.string(), z.number(), z.date()])
8-
.optional()
9-
.transform((value) => (value === undefined ? value : new Date(value)))
10-
.refine((value) => (value === undefined ? value : !isNaN(value.getTime()))),
8+
.transform((value) => new Date(value))
9+
.refine((value) => !isNaN(value.getTime()))
10+
.optional(),
1111
customData: z.string().optional(),
1212
categories: z.array(z.string()).optional(),
1313
author: z.string().optional(),

packages/astro-rss/test/rss.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
phpFeedItem,
1111
phpFeedItemWithContent,
1212
phpFeedItemWithCustomData,
13+
phpFeedItemWithoutDate,
1314
site,
1415
title,
1516
web1FeedItem,
@@ -25,6 +26,8 @@ const validXmlResult = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
2526
// biome-ignore format: keep in one line
2627
const validXmlWithContentResult = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title><![CDATA[${title}]]></title><description><![CDATA[${description}]]></description><link>${site}/</link><item><title><![CDATA[${phpFeedItemWithContent.title}]]></title><link>${site}${phpFeedItemWithContent.link}/</link><guid isPermaLink="true">${site}${phpFeedItemWithContent.link}/</guid><description><![CDATA[${phpFeedItemWithContent.description}]]></description><pubDate>${new Date(phpFeedItemWithContent.pubDate).toUTCString()}</pubDate><content:encoded><![CDATA[${phpFeedItemWithContent.content}]]></content:encoded></item><item><title><![CDATA[${web1FeedItemWithContent.title}]]></title><link>${site}${web1FeedItemWithContent.link}/</link><guid isPermaLink="true">${site}${web1FeedItemWithContent.link}/</guid><description><![CDATA[${web1FeedItemWithContent.description}]]></description><pubDate>${new Date(web1FeedItemWithContent.pubDate).toUTCString()}</pubDate><content:encoded><![CDATA[${web1FeedItemWithContent.content}]]></content:encoded></item></channel></rss>`;
2728
// biome-ignore format: keep in one line
29+
const validXmlResultWithMissingDate = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title><![CDATA[${title}]]></title><description><![CDATA[${description}]]></description><link>${site}/</link><item><title><![CDATA[${phpFeedItemWithoutDate.title}]]></title><link>${site}${phpFeedItemWithoutDate.link}/</link><guid isPermaLink="true">${site}${phpFeedItemWithoutDate.link}/</guid><description><![CDATA[${phpFeedItemWithoutDate.description}]]></description></item><item><title><![CDATA[${phpFeedItem.title}]]></title><link>${site}${phpFeedItem.link}/</link><guid isPermaLink="true">${site}${phpFeedItem.link}/</guid><description><![CDATA[${phpFeedItem.description}]]></description><pubDate>${new Date(phpFeedItem.pubDate).toUTCString()}</pubDate></item></channel></rss>`;
30+
// biome-ignore format: keep in one line
2831
const validXmlResultWithAllData = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title><![CDATA[${title}]]></title><description><![CDATA[${description}]]></description><link>${site}/</link><item><title><![CDATA[${phpFeedItem.title}]]></title><link>${site}${phpFeedItem.link}/</link><guid isPermaLink="true">${site}${phpFeedItem.link}/</guid><description><![CDATA[${phpFeedItem.description}]]></description><pubDate>${new Date(phpFeedItem.pubDate).toUTCString()}</pubDate></item><item><title><![CDATA[${web1FeedItemWithAllData.title}]]></title><link>${site}${web1FeedItemWithAllData.link}/</link><guid isPermaLink="true">${site}${web1FeedItemWithAllData.link}/</guid><description><![CDATA[${web1FeedItemWithAllData.description}]]></description><pubDate>${new Date(web1FeedItemWithAllData.pubDate).toUTCString()}</pubDate><category>${web1FeedItemWithAllData.categories[0]}</category><category>${web1FeedItemWithAllData.categories[1]}</category><author>${web1FeedItemWithAllData.author}</author><comments>${web1FeedItemWithAllData.commentsUrl}</comments><source url="${web1FeedItemWithAllData.source.url}">${web1FeedItemWithAllData.source.title}</source><enclosure url="${site}${web1FeedItemWithAllData.enclosure.url}" length="${web1FeedItemWithAllData.enclosure.length}" type="${web1FeedItemWithAllData.enclosure.type}"/></item></channel></rss>`;
2932
// biome-ignore format: keep in one line
3033
const validXmlWithCustomDataResult = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title><![CDATA[${title}]]></title><description><![CDATA[${description}]]></description><link>${site}/</link><item><title><![CDATA[${phpFeedItemWithCustomData.title}]]></title><link>${site}${phpFeedItemWithCustomData.link}/</link><guid isPermaLink="true">${site}${phpFeedItemWithCustomData.link}/</guid><description><![CDATA[${phpFeedItemWithCustomData.description}]]></description><pubDate>${new Date(phpFeedItemWithCustomData.pubDate).toUTCString()}</pubDate>${phpFeedItemWithCustomData.customData}</item><item><title><![CDATA[${web1FeedItemWithContent.title}]]></title><link>${site}${web1FeedItemWithContent.link}/</link><guid isPermaLink="true">${site}${web1FeedItemWithContent.link}/</guid><description><![CDATA[${web1FeedItemWithContent.description}]]></description><pubDate>${new Date(web1FeedItemWithContent.pubDate).toUTCString()}</pubDate><content:encoded><![CDATA[${web1FeedItemWithContent.content}]]></content:encoded></item></channel></rss>`;
@@ -101,6 +104,17 @@ describe('getRssString', () => {
101104
assertXmlDeepEqual(str, validXmlWithContentResult);
102105
});
103106

107+
it('should generate on valid RSSFeedItem array with missing date', async () => {
108+
const str = await getRssString({
109+
title,
110+
description,
111+
items: [phpFeedItemWithoutDate, phpFeedItem],
112+
site,
113+
});
114+
115+
assertXmlDeepEqual(str, validXmlResultWithMissingDate);
116+
});
117+
104118
it('should generate on valid RSSFeedItem array with all RSS content included', async () => {
105119
const str = await getRssString({
106120
title,

packages/astro-rss/test/test-utils.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ export const title = 'My RSS feed';
44
export const description = 'This sure is a nice RSS feed';
55
export const site = 'https://example.com';
66

7-
export const phpFeedItem = {
7+
export const phpFeedItemWithoutDate = {
88
link: '/php',
99
title: 'Remember PHP?',
10-
pubDate: '1994-05-03',
1110
description:
1211
'PHP is a general-purpose scripting language geared toward web development. It was originally created by Danish-Canadian programmer Rasmus Lerdorf in 1994.',
1312
};
13+
export const phpFeedItem = {
14+
...phpFeedItemWithoutDate,
15+
pubDate: '1994-05-03',
16+
};
1417
export const phpFeedItemWithContent = {
1518
...phpFeedItem,
1619
content: `<h1>${phpFeedItem.title}</h1><p>${phpFeedItem.description}</p>`,

0 commit comments

Comments
 (0)