Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update rss package to return a response #8198

Merged
merged 3 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/honest-houses-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'@astrojs/rss': major
---

Update the `rss()` default export to return a `Response` instead of a simple object, which is deprecated in Astro 3.0. If you were directly returning the `rss()` result from an endpoint before, this breaking change should not affect you.

You can also import `getRssString()` to get the RSS string directly and use it to return your own Response:

```ts
// src/pages/rss.xml.js
import { getRssString } from '@astrojs/rss';

export async function get(context) {
const rssString = await getRssString({
title: 'Buzz’s Blog',
...
});

return new Response(rssString, {
headers: {
'Content-Type': 'application/xml',
},
});
}
```
22 changes: 21 additions & 1 deletion packages/astro-rss/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,27 @@ export async function get(context) {
}
```

---
## `getRssString()`

As `rss()` returns a `Response`, you can also use `getRssString()` to get the RSS string directly and use it in your own response:

```ts "getRssString"
// src/pages/rss.xml.js
import { getRssString } from '@astrojs/rss';

export async function get(context) {
const rssString = await getRssString({
title: 'Buzz’s Blog',
...
});

return new Response(rssString, {
headers: {
'Content-Type': 'application/xml',
},
});
}
```

For more on building with Astro, [visit the Astro docs][astro-rss].

Expand Down
16 changes: 11 additions & 5 deletions packages/astro-rss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,18 @@ const rssOptionsValidator = z.object({
trailingSlash: z.boolean().default(true),
});

export default async function getRSS(rssOptions: RSSOptions) {
const validatedRssOptions = await validateRssOptions(rssOptions);
export default async function getRssResponse(rssOptions: RSSOptions): Promise<Response> {
const rssString = await getRssString(rssOptions);
return new Response(rssString, {
headers: {
'Content-Type': 'application/xml',
},
});
}

return {
body: await generateRSS(validatedRssOptions),
};
export async function getRssString(rssOptions: RSSOptions): Promise<string> {
const validatedRssOptions = await validateRssOptions(rssOptions);
return await generateRSS(validatedRssOptions);
}

async function validateRssOptions(rssOptions: RSSOptions) {
Expand Down
80 changes: 56 additions & 24 deletions packages/astro-rss/test/rss.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import rss from '../dist/index.js';
import rss, { getRssString } from '../dist/index.js';
import { rssSchema } from '../dist/schema.js';
import chai from 'chai';
import chaiPromises from 'chai-as-promised';
Expand Down Expand Up @@ -36,41 +36,73 @@ const validXmlWithStylesheet = `<?xml version="1.0" encoding="UTF-8"?><?xml-styl
const validXmlWithXSLStylesheet = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/feedstylesheet.xsl" type="text/xsl"?><rss version="2.0"><channel><title><![CDATA[${title}]]></title><description><![CDATA[${description}]]></description><link>${site}/</link></channel></rss>`;

describe('rss', () => {
it('should return a response', async () => {
const response = await rss({
title,
description,
items: [phpFeedItem, web1FeedItem],
site,
});

const str = await response.text();
chai.expect(str).xml.to.equal(validXmlResult);

const contentType = response.headers.get('Content-Type');
chai.expect(contentType).to.equal('application/xml');
});

it('should be the same string as getRssString', async () => {
const options = {
title,
description,
items: [phpFeedItem, web1FeedItem],
site,
};

const response = await rss(options);
const str1 = await response.text();
const str2 = await getRssString(options);

chai.expect(str1).to.equal(str2);
});
});

describe('getRssString', () => {
it('should generate on valid RSSFeedItem array', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [phpFeedItem, web1FeedItem],
site,
});

chai.expect(body).xml.to.equal(validXmlResult);
chai.expect(str).xml.to.equal(validXmlResult);
});

it('should generate on valid RSSFeedItem array with HTML content included', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [phpFeedItemWithContent, web1FeedItemWithContent],
site,
});

chai.expect(body).xml.to.equal(validXmlWithContentResult);
chai.expect(str).xml.to.equal(validXmlWithContentResult);
});

it('should generate on valid RSSFeedItem array with all RSS content included', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [phpFeedItem, web1FeedItemWithAllData],
site,
});

chai.expect(body).xml.to.equal(validXmlResultWithAllData);
chai.expect(str).xml.to.equal(validXmlResultWithAllData);
});

it('should generate on valid RSSFeedItem array with custom data included', async () => {
const { body } = await rss({
const str = await getRssString({
xmlns: {
dc: 'http://purl.org/dc/elements/1.1/',
},
Expand All @@ -80,37 +112,37 @@ describe('rss', () => {
site,
});

chai.expect(body).xml.to.equal(validXmlWithCustomDataResult);
chai.expect(str).xml.to.equal(validXmlWithCustomDataResult);
});

it('should include xml-stylesheet instruction when stylesheet is defined', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [],
site,
stylesheet: '/feedstylesheet.css',
});

chai.expect(body).xml.to.equal(validXmlWithStylesheet);
chai.expect(str).xml.to.equal(validXmlWithStylesheet);
});

it('should include xml-stylesheet instruction with xsl type when stylesheet is set to xsl file', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [],
site,
stylesheet: '/feedstylesheet.xsl',
});

chai.expect(body).xml.to.equal(validXmlWithXSLStylesheet);
chai.expect(str).xml.to.equal(validXmlWithXSLStylesheet);
});

it('should preserve self-closing tags on `customData`', async () => {
const customData =
'<atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/>';
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [],
Expand All @@ -121,34 +153,34 @@ describe('rss', () => {
customData,
});

chai.expect(body).to.contain(customData);
chai.expect(str).to.contain(customData);
});

it('should filter out entries marked as `draft`', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
site,
});

chai.expect(body).xml.to.equal(validXmlWithoutWeb1FeedResult);
chai.expect(str).xml.to.equal(validXmlWithoutWeb1FeedResult);
});

it('should respect drafts option', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
site,
drafts: true,
});

chai.expect(body).xml.to.equal(validXmlResult);
chai.expect(str).xml.to.equal(validXmlResult);
});

it('should not append trailing slash to URLs with the given option', async () => {
const { body } = await rss({
const str = await getRssString({
title,
description,
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
Expand All @@ -157,8 +189,8 @@ describe('rss', () => {
trailingSlash: false,
});

chai.expect(body).xml.to.contain('https://example.com/<');
chai.expect(body).xml.to.contain('https://example.com/php<');
chai.expect(str).xml.to.contain('https://example.com/<');
chai.expect(str).xml.to.contain('https://example.com/php<');
});

it('Deprecated import.meta.glob mapping still works', async () => {
Expand Down Expand Up @@ -187,14 +219,14 @@ describe('rss', () => {
),
};

const { body } = await rss({
const str = await getRssString({
title,
description,
items: globResult,
site,
});

chai.expect(body).xml.to.equal(validXmlResult);
chai.expect(str).xml.to.equal(validXmlResult);
});

it('should fail when an invalid date string is provided', async () => {
Expand Down