Skip to content

[RSS] Get ready for content collections #5851

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

Merged
merged 37 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3c3844c
chore: strictNullChecks for zod
bholmesdev Jan 12, 2023
90756d7
feat: expose `rssSchema` helper
bholmesdev Jan 12, 2023
00de0ca
refactor: align types with schema types
bholmesdev Jan 12, 2023
817efe2
feat: break glob handler to globToRssItems util
bholmesdev Jan 12, 2023
90ba1bb
refactor: RSS options validation to Zod
bholmesdev Jan 12, 2023
05e9792
refactor: avoid intermediate type
bholmesdev Jan 12, 2023
564fea5
fix: allow numbers and dates in pubDate
bholmesdev Jan 12, 2023
70d4489
test: update glob and error tests
bholmesdev Jan 12, 2023
bd73f00
feat: add rss to with-content starter
bholmesdev Jan 12, 2023
0bbd403
fix: move globToRssItems back to internal behavior
bholmesdev Jan 13, 2023
017f636
chore: JSON.stringify
bholmesdev Jan 13, 2023
dc19121
Revert "fix: move globToRssItems back to internal behavior"
bholmesdev Jan 13, 2023
c8f7a3a
test: missing url
bholmesdev Jan 13, 2023
edb3017
docs: `import.meta.env.SITE` -> `context.site`
bholmesdev Jan 13, 2023
3474987
docs: update README to content collections example
bholmesdev Jan 13, 2023
f1052e6
fix: url -> link
bholmesdev Jan 13, 2023
508567d
docs: add `rssSchema` to README
bholmesdev Jan 13, 2023
f99e6ce
chore: consistent formatting
bholmesdev Jan 13, 2023
1280cf1
docs: add `pagesGlobToRssItems()` reference
bholmesdev Jan 13, 2023
9e9aa04
chore: globToRssItems -> pagesGlobToRssItems
bholmesdev Jan 13, 2023
62d40f2
chore: changeset
bholmesdev Jan 13, 2023
9c5e358
fix: bad docs line highlighting
bholmesdev Jan 13, 2023
f690a15
fix: add collections export to example
bholmesdev Jan 13, 2023
848f631
nit: remove "our"
bholmesdev Jan 13, 2023
35c55a4
fix: are -> all
bholmesdev Jan 13, 2023
905f181
fix: more README edits
bholmesdev Jan 13, 2023
146da89
deps: kleur
bholmesdev Jan 13, 2023
d8ab5a7
chore: add back import.meta.glob handling as deprecated
bholmesdev Jan 13, 2023
adce64a
docs: bump down to `minor`, update headline to be less content collec…
bholmesdev Jan 13, 2023
fe922e4
typo: suggest adding
bholmesdev Jan 17, 2023
93649b5
chore: support URL object on `site`
bholmesdev Jan 17, 2023
c739709
docs: add await to pagesGlob ex
bholmesdev Jan 17, 2023
eb13a44
docs: tighten `rssSchema` explainer
bholmesdev Jan 19, 2023
85eb84f
docs: tighten pagesGlobToRssItems section
bholmesdev Jan 19, 2023
23858f5
docs: add content to README
bholmesdev Jan 19, 2023
f2d9652
docs: replace examples with docs link
bholmesdev Jan 19, 2023
a3cd5e9
docs: re-we
bholmesdev Jan 19, 2023
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
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