Skip to content

Commit

Permalink
[RSS] Get ready for content collections (#5851)
Browse files Browse the repository at this point in the history
* chore: strictNullChecks for zod

* feat: expose `rssSchema` helper

* refactor: align types with schema types

* feat: break glob handler to globToRssItems util

* refactor: RSS options validation to Zod

* refactor: avoid intermediate type

* fix: allow numbers and dates in pubDate

* test: update glob and error tests

* feat: add rss to with-content starter

* fix: move globToRssItems back to internal behavior

* chore: JSON.stringify

* Revert "fix: move globToRssItems back to internal behavior"

This reverts commit 8530507.

* test: missing url

* docs: `import.meta.env.SITE` -> `context.site`

* docs: update README to content collections example

* fix: url -> link

* docs: add `rssSchema` to README

* chore: consistent formatting

* docs: add `pagesGlobToRssItems()` reference

* chore: globToRssItems -> pagesGlobToRssItems

* chore: changeset

* fix: bad docs line highlighting

* fix: add collections export to example

* nit: remove "our"

* fix: are -> all

* fix: more README edits

* deps: kleur

* chore: add back import.meta.glob handling as deprecated

* docs: bump down to `minor`, update headline to be less content collections-y

* typo: suggest adding

* chore: support URL object on `site`

* docs: add await to pagesGlob ex

* docs: tighten `rssSchema` explainer

* docs: tighten pagesGlobToRssItems section

* docs: add content to README

* docs: replace examples with docs link

* docs: re-we

Co-authored-by: Sarah Rainsberger <[email protected]>

Co-authored-by: Sarah Rainsberger <[email protected]>
  • Loading branch information
bholmesdev and sarah11918 authored Jan 19, 2023
1 parent 97267e3 commit 81dce94
Show file tree
Hide file tree
Showing 12 changed files with 456 additions and 381 deletions.
41 changes: 41 additions & 0 deletions .changeset/fluffy-cups-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
'@astrojs/rss': minor
---

Update RSS config for readability and consistency with Astro 2.0.

#### Migration - `import.meta.glob()` handling

We have deprecated `items: import.meta.glob(...)` handling in favor of a separate `pagesGlobToRssItems()` helper. This simplifies our `items` configuration option to accept a single type, without losing existing functionality.

If you rely on our `import.meta.glob()` handling, we suggest adding the `pagesGlobToRssItems()` wrapper to your RSS config:

```diff
// src/pages/rss.xml.js
import rss, {
+ pagesGlobToRssItems
} from '@astrojs/rss';

export function get(context) {
return rss({
+ items: pagesGlobToRssItems(
import.meta.glob('./blog/*.{md,mdx}'),
+ ),
});
}
```

#### New `rssSchema` for content collections

`@astrojs/rss` now exposes an `rssSchema` for use with content collections. This ensures all RSS feed properties are present in your frontmatter:

```ts
import { defineCollection } from 'astro:content';
import { rssSchema } from '@astrojs/rss';

const blog = defineCollection({
schema: rssSchema,
});

export const collections = { blog };
```
2 changes: 1 addition & 1 deletion examples/blog/src/pages/rss.xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export async function get(context) {
return rss({
title: SITE_TITLE,
description: SITE_DESCRIPTION,
site: context.site.href,
site: context.site,
items: posts.map((post) => ({
...post.data,
link: `/blog/${post.slug}/`,
Expand Down
135 changes: 108 additions & 27 deletions packages/astro-rss/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,62 @@ pnpm i @astrojs/rss

The `@astrojs/rss` package provides helpers for generating RSS feeds within [Astro endpoints][astro-endpoints]. This unlocks both static builds _and_ on-demand generation when using an [SSR adapter](https://docs.astro.build/en/guides/server-side-rendering/#enabling-ssr-in-your-project).

For instance, say you need to generate an RSS feed for all posts under `src/pages/blog/`. Start by [adding a `site` to your project's `astro.config` for link generation](https://docs.astro.build/en/reference/configuration-reference/#site). Then, create an `rss.xml.js` file under your project's `src/pages/` directory, and use [Vite's `import.meta.glob` helper](https://vitejs.dev/guide/features.html#glob-import) like so:
For instance, say you need to generate an RSS feed for all posts under `src/content/blog/` using content collections.

Start by [adding a `site` to your project's `astro.config` for link generation](https://docs.astro.build/en/reference/configuration-reference/#site). Then, create an `rss.xml.js` file under your project's `src/pages/` directory, and [use `getCollection()`](https://docs.astro.build/en/guides/content-collections/#getcollection) to generate a feed from all documents in the `blog` collection:

```js
// src/pages/rss.xml.js
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export const get = () => rss({
export async function get(context) {
const posts = await getCollection('blog');
return rss({
title: 'Buzz’s Blog',
description: 'A humble Astronaut’s guide to the stars',
// pull in the "site" from your project's astro.config
site: import.meta.env.SITE,
items: import.meta.glob('./blog/**/*.md'),
// Pull in your project "site" from the endpoint context
// https://docs.astro.build/en/reference/api-reference/#contextsite
site: context.site,
items: posts.map(post => ({
// Assumes all RSS feed item properties are in post frontmatter
...post.data,
// Generate a `url` from each post `slug`
// This assumes all blog posts are rendered as `/blog/[slug]` routes
// https://docs.astro.build/en/guides/content-collections/#generating-pages-from-content-collections
link: `/blog/${post.slug}/`,
}))
});
}
```

Read **[Astro's RSS docs][astro-rss]** for full usage examples.
Read **[Astro's RSS docs][astro-rss]** for more on using content collections, and instructions for globbing entries in `/src/pages/`.

## `rss()` configuration options

The `rss` default export offers a number of configuration options. Here's a quick reference:

```js
rss({
// `<title>` field in output xml
title: 'Buzz’s Blog',
// `<description>` field in output xml
description: 'A humble Astronaut’s guide to the stars',
// provide a base URL for RSS <item> links
site: import.meta.env.SITE,
// list of `<item>`s in output xml
items: import.meta.glob('./**/*.md'),
// include draft posts in the feed (default: false)
drafts: true,
// (optional) absolute path to XSL stylesheet in your project
stylesheet: '/rss-styles.xsl',
// (optional) inject custom xml
customData: '<language>en-us</language>',
// (optional) add arbitrary metadata to opening <rss> tag
xmlns: { h: 'http://www.w3.org/TR/html4/' },
});
export function get(context) {
return rss({
// `<title>` field in output xml
title: 'Buzz’s Blog',
// `<description>` field in output xml
description: 'A humble Astronaut’s guide to the stars',
// provide a base URL for RSS <item> links
site: context.site,
// list of `<item>`s in output xml
items: [...],
// include draft posts in the feed (default: false)
drafts: true,
// (optional) absolute path to XSL stylesheet in your project
stylesheet: '/rss-styles.xsl',
// (optional) inject custom xml
customData: '<language>en-us</language>',
// (optional) add arbitrary metadata to opening <rss> tag
xmlns: { h: 'http://www.w3.org/TR/html4/' },
});
}
```

### title
Expand All @@ -77,13 +93,22 @@ The `<description>` attribute of your RSS feed's output xml.

Type: `string (required)`

The base URL to use when generating RSS item links. We recommend using `import.meta.env.SITE` to pull in the "site" from your project's astro.config. Still, feel free to use a custom base URL if necessary.
The base URL to use when generating RSS item links. We recommend using the [endpoint context object](https://docs.astro.build/en/reference/api-reference/#contextsite), which includes the `site` configured in your project's `astro.config.*`:

```ts
import rss from '@astrojs/rss';

export const get = (context) => rss({
site: context.site,
...
});
```

### items

Type: `RSSFeedItem[] | GlobResult (required)`
Type: `RSSFeedItem[] (required)`

Either a list of formatted RSS feed items or the result of [Vite's `import.meta.glob` helper](https://vitejs.dev/guide/features.html#glob-import). See [Astro's RSS items documentation](https://docs.astro.build/en/guides/rss/#generating-items) for usage examples to choose the best option for you.
A list of formatted RSS feed items. See [Astro's RSS items documentation](https://docs.astro.build/en/guides/rss/#generating-items) for usage examples to choose the best option for you.

When providing a formatted RSS item list, see the `RSSFeedItem` type reference below:

Expand Down Expand Up @@ -152,6 +177,62 @@ Will inject the following XML:
<rss xmlns:h="http://www.w3.org/TR/html4/"...
```

### content

The `content` key contains the full content of the post as HTML. This allows you to make your entire post content available to RSS feed readers.

**Note:** Whenever you're using HTML content in XML, we suggest using a package like [`sanitize-html`](https://www.npmjs.com/package/sanitize-html) in order to make sure that your content is properly sanitized, escaped, and encoded.
[See our RSS documentation](https://docs.astro.build/en/guides/rss/#including-full-post-content) for examples using content collections and glob imports.
## `rssSchema`
When using content collections, you can configure your collection schema to enforce expected [`RSSFeedItem`](#items) properties. Import and apply `rssSchema` to ensure that each collection entry produces a valid RSS feed item:
```ts "schema: rssSchema,"
import { defineCollection } from 'astro:content';
import { rssSchema } from '@astrojs/rss';
const blog = defineCollection({
schema: rssSchema,
});
export const collections = { blog };
```
If you have an existing schema, you can merge extra properties using `extends()`:
```ts ".extends({ extraProperty: z.string() }),"
import { defineCollection } from 'astro:content';
import { rssSchema } from '@astrojs/rss';
const blog = defineCollection({
schema: rssSchema.extends({ extraProperty: z.string() }),
});
```
## `pagesGlobToRssItems()`
To create an RSS feed from documents in `src/pages/`, use the `pagesGlobToRssItems()` helper. This accepts an `import.meta.glob` result ([see Vite documentation](https://vitejs.dev/guide/features.html#glob-import)) and outputs an array of valid [`RSSFeedItem`s](#items).
This function assumes, but does not verify, you are globbing for items inside `src/pages/`, and all necessary feed properties are present in each document's frontmatter. If you encounter errors, verify each page frontmatter manually.

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

export async function get(context) {
return rss({
title: 'Buzz’s Blog',
description: 'A humble Astronaut’s guide to the stars',
site: context.site,
items: await pagesGlobToRssItems(
import.meta.glob('./blog/*.{md,mdx}'),
),
});
}
```

---

For more on building with Astro, [visit the Astro docs][astro-rss].
Expand Down
3 changes: 2 additions & 1 deletion packages/astro-rss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"mocha": "^9.2.2"
},
"dependencies": {
"fast-xml-parser": "^4.0.8"
"fast-xml-parser": "^4.0.8",
"kleur": "^4.1.5"
}
}
Loading

0 comments on commit 81dce94

Please sign in to comment.