@ampless/plugin-rss 0.2.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +76 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ampless contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @ampless/plugin-rss
|
|
2
|
+
|
|
3
|
+
RSS 2.0 feed plugin for [ampless](https://github.com/heavymoons/ampless).
|
|
4
|
+
|
|
5
|
+
> **Pre-release / alpha.** Breaking changes possible in any minor version until v1.0.
|
|
6
|
+
|
|
7
|
+
Regenerates `feed.xml` to S3 every time a post is published, unpublished, updated, or deleted. The Next.js `/feed.xml` route serves the latest version through.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @ampless/plugin-rss@alpha
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Configure
|
|
16
|
+
|
|
17
|
+
In `cms.config.ts`:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { defineConfig } from 'ampless'
|
|
21
|
+
import rssPlugin from '@ampless/plugin-rss'
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
// ...
|
|
25
|
+
plugins: [
|
|
26
|
+
rssPlugin({
|
|
27
|
+
limit: 20, // most recent posts
|
|
28
|
+
language: 'en', // BCP 47 language tag
|
|
29
|
+
// siteUrl: 'https://example.com',
|
|
30
|
+
// feedPath: '/feed.xml',
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Option | Default | Notes |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `limit` | `20` | Number of newest published posts to include |
|
|
39
|
+
| `language` | `'en'` | RSS `<language>` tag (BCP 47) |
|
|
40
|
+
| `siteUrl` | `site.url` | Override the base URL (e.g. for staging environments) |
|
|
41
|
+
| `feedPath` | `/feed.xml` | Path the feed is served at; emitted in the `<atom:link rel="self">` element |
|
|
42
|
+
|
|
43
|
+
The plugin also adds an autodiscovery `<link rel="alternate" type="application/rss+xml">` to your site's `<head>` via the `siteMetadata` hook.
|
|
44
|
+
|
|
45
|
+
## Trust level
|
|
46
|
+
|
|
47
|
+
`trusted` — runs in the trusted Lambda processor with read access to the post table and write access under `public/plugins/rss/` in the site's S3 bucket.
|
|
48
|
+
|
|
49
|
+
## Hooks
|
|
50
|
+
|
|
51
|
+
Subscribes to:
|
|
52
|
+
|
|
53
|
+
- `content.published`
|
|
54
|
+
- `content.unpublished`
|
|
55
|
+
- `content.deleted`
|
|
56
|
+
- `content.updated` (so a re-titled published post produces a fresh `<title>` in the feed)
|
|
57
|
+
|
|
58
|
+
Each hook re-reads the full set of published posts and writes the regenerated XML to `s3://<site-bucket>/public/plugins/rss/feed.xml`. The Next.js route reads from there, so the feed is always at most one event behind real time.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Post, Config, AmplessPlugin } from 'ampless';
|
|
2
|
+
|
|
3
|
+
interface RssFeedOptions {
|
|
4
|
+
/** Number of most recent posts to include. Default 20. */
|
|
5
|
+
limit?: number;
|
|
6
|
+
/** Override site URL (e.g. for staging). Defaults to config.site.url. */
|
|
7
|
+
siteUrl?: string;
|
|
8
|
+
/** Path to expose the feed at. Default '/feed.xml'. */
|
|
9
|
+
feedPath?: string;
|
|
10
|
+
/** RSS <language> tag (BCP 47). Default 'en'. */
|
|
11
|
+
language?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function buildRssFeed(posts: Post[], site: Config['site'], options?: RssFeedOptions): string;
|
|
14
|
+
|
|
15
|
+
interface RssPluginOptions extends RssFeedOptions {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* RSS / Atom feed plugin.
|
|
19
|
+
*
|
|
20
|
+
* On `content.published`, `content.unpublished`, and `content.deleted`
|
|
21
|
+
* events, regenerates the site feed from all currently-published posts and
|
|
22
|
+
* stores it at `public/plugins/rss/feed.xml`. The Next.js `/feed.xml`
|
|
23
|
+
* route serves this object.
|
|
24
|
+
*
|
|
25
|
+
* Adds a <link rel="alternate" type="application/rss+xml"> to the site
|
|
26
|
+
* metadata so feed readers can autodiscover.
|
|
27
|
+
*/
|
|
28
|
+
declare function rssPlugin(options?: RssPluginOptions): AmplessPlugin;
|
|
29
|
+
|
|
30
|
+
export { type RssFeedOptions, type RssPluginOptions, buildRssFeed, rssPlugin as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePlugin } from "ampless";
|
|
3
|
+
|
|
4
|
+
// src/feed.ts
|
|
5
|
+
import { escapeXml } from "ampless";
|
|
6
|
+
var toRfc822 = (iso) => new Date(iso).toUTCString();
|
|
7
|
+
function buildRssFeed(posts, site, options = {}) {
|
|
8
|
+
const baseUrl = (options.siteUrl ?? site.url).replace(/\/$/, "");
|
|
9
|
+
const feedPath = options.feedPath ?? "/feed.xml";
|
|
10
|
+
const limit = options.limit ?? 20;
|
|
11
|
+
const language = options.language ?? "en";
|
|
12
|
+
const items = posts.filter((p) => p.status === "published").slice(0, limit).map((post) => {
|
|
13
|
+
const url = `${baseUrl}/${post.slug}`;
|
|
14
|
+
const pubDate = post.publishedAt ? toRfc822(post.publishedAt) : (/* @__PURE__ */ new Date()).toUTCString();
|
|
15
|
+
const description = post.excerpt ?? "";
|
|
16
|
+
const tags = (post.tags ?? []).map((t) => ` <category>${escapeXml(t)}</category>`).join("\n");
|
|
17
|
+
return ` <item>
|
|
18
|
+
<title>${escapeXml(post.title)}</title>
|
|
19
|
+
<link>${escapeXml(url)}</link>
|
|
20
|
+
<guid isPermaLink="true">${escapeXml(url)}</guid>
|
|
21
|
+
<pubDate>${pubDate}</pubDate>
|
|
22
|
+
${tags ? tags + "\n" : ""} <description>${escapeXml(description)}</description>
|
|
23
|
+
</item>`;
|
|
24
|
+
}).join("\n");
|
|
25
|
+
const lastBuild = (/* @__PURE__ */ new Date()).toUTCString();
|
|
26
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
27
|
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
28
|
+
<channel>
|
|
29
|
+
<title>${escapeXml(site.name)}</title>
|
|
30
|
+
<link>${escapeXml(baseUrl)}</link>
|
|
31
|
+
<description>${escapeXml(site.description ?? site.name)}</description>
|
|
32
|
+
<language>${escapeXml(language)}</language>
|
|
33
|
+
<atom:link href="${escapeXml(baseUrl + feedPath)}" rel="self" type="application/rss+xml" />
|
|
34
|
+
<lastBuildDate>${lastBuild}</lastBuildDate>
|
|
35
|
+
${items}
|
|
36
|
+
</channel>
|
|
37
|
+
</rss>
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/index.ts
|
|
42
|
+
function rssPlugin(options = {}) {
|
|
43
|
+
const feedPath = options.feedPath ?? "/feed.xml";
|
|
44
|
+
async function rebuild(_event, ctx) {
|
|
45
|
+
const posts = await ctx.listPublishedPosts();
|
|
46
|
+
const xml = buildRssFeed(posts, ctx.site, options);
|
|
47
|
+
await ctx.writePublicAsset("feed.xml", xml, "application/rss+xml; charset=utf-8");
|
|
48
|
+
}
|
|
49
|
+
return definePlugin({
|
|
50
|
+
name: "rss",
|
|
51
|
+
apiVersion: 1,
|
|
52
|
+
trust_level: "trusted",
|
|
53
|
+
hooks: {
|
|
54
|
+
"content.published": rebuild,
|
|
55
|
+
"content.unpublished": rebuild,
|
|
56
|
+
"content.deleted": rebuild,
|
|
57
|
+
// Updates can change title/excerpt of an already-published post, so
|
|
58
|
+
// we regenerate on every update too.
|
|
59
|
+
"content.updated": rebuild
|
|
60
|
+
},
|
|
61
|
+
siteMetadata(site) {
|
|
62
|
+
const baseUrl = (options.siteUrl ?? site.url).replace(/\/$/, "");
|
|
63
|
+
return {
|
|
64
|
+
alternates: {
|
|
65
|
+
types: {
|
|
66
|
+
"application/rss+xml": baseUrl + feedPath
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
buildRssFeed,
|
|
75
|
+
rssPlugin as default
|
|
76
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ampless/plugin-rss",
|
|
3
|
+
"version": "0.2.0-alpha.0",
|
|
4
|
+
"description": "RSS / Atom feed plugin for ampless",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/heavymoons/ampless.git",
|
|
23
|
+
"directory": "packages/plugin-rss"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/heavymoons/ampless/tree/main/packages/plugin-rss#readme",
|
|
26
|
+
"bugs": "https://github.com/heavymoons/ampless/issues",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ampless": "0.2.0-alpha.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"ampless",
|
|
32
|
+
"plugin",
|
|
33
|
+
"rss",
|
|
34
|
+
"atom",
|
|
35
|
+
"feed"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"lint": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run --passWithNoTests"
|
|
42
|
+
}
|
|
43
|
+
}
|