Update rss package to return a response (#8198)
This commit is contained in:
parent
353dcb23e8
commit
cb95aa5f8e
4 changed files with 114 additions and 31 deletions
25
.changeset/honest-houses-deny.md
Normal file
25
.changeset/honest-houses-deny.md
Normal file
|
@ -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',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
|
@ -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].
|
For more on building with Astro, [visit the Astro docs][astro-rss].
|
||||||
|
|
||||||
|
|
|
@ -98,12 +98,18 @@ const rssOptionsValidator = z.object({
|
||||||
trailingSlash: z.boolean().default(true),
|
trailingSlash: z.boolean().default(true),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default async function getRSS(rssOptions: RSSOptions) {
|
export default async function getRssResponse(rssOptions: RSSOptions): Promise<Response> {
|
||||||
const validatedRssOptions = await validateRssOptions(rssOptions);
|
const rssString = await getRssString(rssOptions);
|
||||||
|
return new Response(rssString, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/xml',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
export async function getRssString(rssOptions: RSSOptions): Promise<string> {
|
||||||
body: await generateRSS(validatedRssOptions),
|
const validatedRssOptions = await validateRssOptions(rssOptions);
|
||||||
};
|
return await generateRSS(validatedRssOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateRssOptions(rssOptions: RSSOptions) {
|
async function validateRssOptions(rssOptions: RSSOptions) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import rss from '../dist/index.js';
|
import rss, { getRssString } from '../dist/index.js';
|
||||||
import { rssSchema } from '../dist/schema.js';
|
import { rssSchema } from '../dist/schema.js';
|
||||||
import chai from 'chai';
|
import chai from 'chai';
|
||||||
import chaiPromises from 'chai-as-promised';
|
import chaiPromises from 'chai-as-promised';
|
||||||
|
@ -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>`;
|
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', () => {
|
describe('rss', () => {
|
||||||
it('should generate on valid RSSFeedItem array', async () => {
|
it('should return a response', async () => {
|
||||||
const { body } = await rss({
|
const response = await rss({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [phpFeedItem, web1FeedItem],
|
items: [phpFeedItem, web1FeedItem],
|
||||||
site,
|
site,
|
||||||
});
|
});
|
||||||
|
|
||||||
chai.expect(body).xml.to.equal(validXmlResult);
|
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 str = await getRssString({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
items: [phpFeedItem, web1FeedItem],
|
||||||
|
site,
|
||||||
|
});
|
||||||
|
|
||||||
|
chai.expect(str).xml.to.equal(validXmlResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate on valid RSSFeedItem array with HTML content included', async () => {
|
it('should generate on valid RSSFeedItem array with HTML content included', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [phpFeedItemWithContent, web1FeedItemWithContent],
|
items: [phpFeedItemWithContent, web1FeedItemWithContent],
|
||||||
site,
|
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 () => {
|
it('should generate on valid RSSFeedItem array with all RSS content included', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [phpFeedItem, web1FeedItemWithAllData],
|
items: [phpFeedItem, web1FeedItemWithAllData],
|
||||||
site,
|
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 () => {
|
it('should generate on valid RSSFeedItem array with custom data included', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
xmlns: {
|
xmlns: {
|
||||||
dc: 'http://purl.org/dc/elements/1.1/',
|
dc: 'http://purl.org/dc/elements/1.1/',
|
||||||
},
|
},
|
||||||
|
@ -80,11 +112,11 @@ describe('rss', () => {
|
||||||
site,
|
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 () => {
|
it('should include xml-stylesheet instruction when stylesheet is defined', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [],
|
items: [],
|
||||||
|
@ -92,11 +124,11 @@ describe('rss', () => {
|
||||||
stylesheet: '/feedstylesheet.css',
|
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 () => {
|
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,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [],
|
items: [],
|
||||||
|
@ -104,13 +136,13 @@ describe('rss', () => {
|
||||||
stylesheet: '/feedstylesheet.xsl',
|
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 () => {
|
it('should preserve self-closing tags on `customData`', async () => {
|
||||||
const customData =
|
const customData =
|
||||||
'<atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/>';
|
'<atom:link href="https://example.com/feed.xml" rel="self" type="application/rss+xml"/>';
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [],
|
items: [],
|
||||||
|
@ -121,22 +153,22 @@ describe('rss', () => {
|
||||||
customData,
|
customData,
|
||||||
});
|
});
|
||||||
|
|
||||||
chai.expect(body).to.contain(customData);
|
chai.expect(str).to.contain(customData);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter out entries marked as `draft`', async () => {
|
it('should filter out entries marked as `draft`', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
|
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
|
||||||
site,
|
site,
|
||||||
});
|
});
|
||||||
|
|
||||||
chai.expect(body).xml.to.equal(validXmlWithoutWeb1FeedResult);
|
chai.expect(str).xml.to.equal(validXmlWithoutWeb1FeedResult);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should respect drafts option', async () => {
|
it('should respect drafts option', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
|
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
|
||||||
|
@ -144,11 +176,11 @@ describe('rss', () => {
|
||||||
drafts: true,
|
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 () => {
|
it('should not append trailing slash to URLs with the given option', async () => {
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
|
items: [phpFeedItem, { ...web1FeedItem, draft: true }],
|
||||||
|
@ -157,8 +189,8 @@ describe('rss', () => {
|
||||||
trailingSlash: false,
|
trailingSlash: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
chai.expect(body).xml.to.contain('https://example.com/<');
|
chai.expect(str).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/php<');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Deprecated import.meta.glob mapping still works', async () => {
|
it('Deprecated import.meta.glob mapping still works', async () => {
|
||||||
|
@ -187,14 +219,14 @@ describe('rss', () => {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const { body } = await rss({
|
const str = await getRssString({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
items: globResult,
|
items: globResult,
|
||||||
site,
|
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 () => {
|
it('should fail when an invalid date string is provided', async () => {
|
||||||
|
|
Loading…
Reference in a new issue